保护私人版权,尊重他人版权。转载请注明出处并附带页面链接
在面向对象的语言中,多态是继数据抽象和继承之后的第三种基本特征。What is polymorphic?多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。
在向上转型中,可能会缩小接口,但不会比其基类接口更窄。
如何理解多态的作用是消除类型间的耦和关系?
例如有这样一行代码:Apple apple=new Apple(),
等号左右两边必须为相同类型,即一边必须依赖于另外一边,这就叫这个类型间的耦合,而多态的话,假设有一个fruit类型,apple可以赋给它,orange也可以,右边不在依赖于左边,fruit类型知道会有赋值但具体赋什么值和它没有关系,这就叫解耦合,故这就是我所理解的多态消除类型间的耦合关系。
##方法调用绑定
将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定。叫做前期绑定。
解决多态问题的方法为后期绑定,它的含义就在于运行时根据对象的类型进行绑定。后期绑定也成为动态绑定或运行时绑定。如果一种语言想实现后期绑定,就必须有某种机制。以便运行时能判断对象的类型,从而调用的恰当的方法。即,编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以利用。
java中除static方法和final方法之外(private方法属于static方法),其他所有的方法都是后期绑定。
final可以有效关闭动态绑定,但最好还是应该根据设计是否使用final。
##正确的行为
由于java中所有方法为动态绑定,故我们可以编写只与基类打交道的代码,并且代码对所有导出类都可以正确运行。发送消息给对象,让对象自己去判断该做什么。
##多态的缺陷
- 只有非private方法才可以被覆盖;我们需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行。确切来说,在导出类中,对于基类中的private方法,最好采用不同的名字。
- 只有普通的方法调用可以是多态的。如果直接访问某个域,这个访问就会在编译期进行解析。
##构造器与多态
构造器实际上是static方法,只不过该static声明是隐式的。
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能被调用。这样做是有意义的,因为构造器具有一项特殊任务:检查对象是否被正确的构造。导出类一般只能访问自己的成员,不能访问基类的成员(基类成员通常是private类型)。只有基类的构造器才具有恰当的知识和权限来对自己的元素进行初始化。因此,必须令所有的构造器都得到调用,否则就不可能正确构造完整对象。这正是编译器为什么要强制每个导出类部分都必须调用构造器的原因。在导出类的构造器主体中,如果没有明确指定调用某个基类构造器,它就会“默默”地调用默认构造器。如果不存在默认构造器,编译器会报错(一般编译器会自动合成一个默认构造器)。
调用构造器遵照的顺序:
- 调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次的根,然后是下一层导出类,等等,直到最低层的导出类。
- 按声明顺序调用成员的初始化方法。
- 调用导出类的构造器主体。
##继承与清理
通过组合和继承的方法来创建新类时,永远不必担心对象的清理问题,子对象通常都会留给垃圾回收器进行处理。
##构造器内部的多态方法的行为
初始化实际过程:
- 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
- 如前所述那样调用基类构造器。
- 按照声明的顺序调用成员的初始化方法。
- 调用导出类的构造器实体。
##协变返回类型
Java SE5中添加了协变返回类型,它表示在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型。
协变返回类型允许返回更具体的类型。
##用继承进行设计
一条通用准则:“用继承表达行为间的差异,并用字段表达状态上的变化”。设计方式应优先选择组合。
##向下转型与运行时类型识别
由于向上转型会丢失具体的类型信息,所以我们就想,通过向下转型-
也就是在继承层次中向下移动-应该能够获取类型信息。
在Java语言中,所有的转型都会得到检查!所以即使我们只是进行一次普通的加括弧形式的类型转换,在进入运行期时仍然会对其进行检查,以便保证它的确是我们希望的那种类型。若不是则会抛出,ClassCastException(类转型异常)。这种在运行期间对类型进行检查的行为称作”运行时类型识别”(RTTI)。
##In a word
多态意味着不同的状态,不同的形式。在面向对象的程序设计中,我们持有从基类继承而来的相同接口,以及使用借口的不同形式与方法:不同版本的动态绑定。