在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征。
“封装”通过合并特征和行为来创建新的数据类型。
“实现隐藏”则通过将细节“私有化”把接口和实现分离开来。
“多态的作用”:消除类型之间的耦合关系(互相之间有影响联系的一种关系)。
多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要他们是从同一基类导出而来的。这种区别是根据方法行为的不同而表示出来的。
8.1 再论向上转型
向上转型:某对象的引用对其基类型的引用的做法。
8.1.1 忘记对象类型
将基类的引用定义为参数来使用向上转型。
8.2 转机
8.2.1 方法调用绑定
绑定: 将一个方法调用同一个方法主体关联起来。
前期绑定:在程序执行之前进行绑定。
后期绑定:在程序运行时根据对象类型进行绑定(Java中默认的绑定,除了static 和final方法(private方法属于final方法),也称为动态绑定或运行时绑定)
8.2.2 产生正确的行为
“几何形状“(shape)。
8.2.3 可扩展性
多态是一项让程序员“将改变的事务与未变的事物分离开来”的重要技术。
8.2.4 缺陷:“覆盖”私有方法
只有非private方法才可以被覆盖,但是还需要密切注意覆盖private方法的现象,这时编译器虽然不会报错,但是也不会按照我们所期望的来执行。确切的说,在导出类中,对于基类中的private方法,最好采用不同的名字。
8.2.5 缺陷:域与静态方法
直接访问某个域,这个访问将在编译期间进行对域解析才会使用多态。(必须是这个访问对其解析才能成为多态)
如果某个方法时静态的,则这个方法不具有多态。
静态方法是与类,而并非与单个的对象相关联的,所以不具有多态性。
8.3 构造器和多态
构造器实际上是static方法,只不过该static声明是隐式的。所以构造器不具有多态性。
8.3.1 构造器的调用顺序
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。
对象调用构造器遵循的顺序:
1)调用基类构造器。这个步奏会不断反复递归下去,首先是构造这个层次结构的根,
然后是下一层导出类,等等,知道最底层的导出类。
2)按声明顺序调用成员的初始化方法。
3)调用导出类构造器的主体。
8.3.2 继承与清理
组合与继承方法创建的新类通常留给垃圾回收期进行处理。如果需要创建清理方法,需要调用基类中的清理类,否则基类中的清理不会发生。清理顺序应该是继承的逆方向。
8.3.3 构造器内部的多态方法的行为
初始化的实际过程是:
1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
2)如前所述那样调用基类构造器。
3)按照声明的顺序调用成员的初始化方法。
4)调用导出类的构造器主体。
编写构造器的有效准则:用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法。在构造器内唯一能够安全调用的那些方法时基类中的final方法。
8.4 协变返回类型
协变返回类型:在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型。
协变返回类型允许返回子类类型。
8.5 用继承进行设计
引用在运行时可以与另一个不同的对象重新绑定起来。
用继承表达行为间的差异,并用字段表达状态上的变化。
8.5.1 纯继承与扩展
只有在基类中已将建立的方法才可以在导出类中被覆盖。
如果在导出类中添加新的方法,则向上转型时无法调用新的方法。
8.5.2 向下转型与运行时类型识别
向下转型不一定是正确的。
Java中,所有转型都要检查类型,在运行期间对类型进行检查的行为成为“运行时类型识别”(RTTI)。
8.6 总结
多态意味着“不同的形式”。
多态是一种不能单独来看待的特性,相反它只能作为类关系“全景”中的一部分,与其他特性协同工作。
包含个别类的成员和消息,包括类与类之间的共同特性以及它们之间的关系。可以更快的程序开发、更好的代码组织、更好扩展的程序以及更容易的代码维护等。