7: Polymorphism(多态性)
再访上传(upcasting)
把对象的类型忘掉
问题的关键
方法调用的绑定
当绑定发生在程序运行之前时(如果有的话,就是由编译器或连接器负责)被称作“前绑定(early binding)”。
“后绑定(late binding)”的意思是要在程序运行的时候,根据对象的类型来决定该绑定哪个方法。后绑定也被称为“动态绑定(dynamic)”或“运行时绑定(run-time binding)”。
除了static和final方法(private方法隐含有final的意思),Java的所有的方法都采用后绑定。也就是说,通常情况下你不必考虑是不是应该采用后绑定——它是自动的。
产生正确的行为
可扩展性
错误:“覆写”private的方法
只有非private的方法才能被覆写,别用基类的private方法的名字去命名派生类的方法。
抽象类和抽象方法
如果类包含一个或多个抽象方法,那么这个类就必须被定义成abstract的。(否则编译器就会报错了。)
创建一个不包含abstract方法的abstract类,是完全可以的。这种技巧可以用于“不必创建abstract的方法,但是又要禁止别人创建这个类的对象”的场合。
构造函数与多态性
构造函数不是多态的(实际上他们都是static方法,只是声明的时候没有直说)。
构造函数的调用顺序
复杂对象的构造函数的调用顺序:
1. 调用基类的构造函数。这是一个递归过程,因此会先创建继承体系的根,然后是下一级派生类,以此类推,直到最后一个继承类的构造函数。
2. 成员对象按照其声明的顺序进行初始化。
3. 执行继承类的构造函数的正文。
继承与清理
对象与对象之间有可能会有依赖关系,因此清理的顺序应该与初始化的顺序相反。对数据成员而言,这就是说它们的清理顺序应该与声明的顺序相反(因为数据的初始化是按照声明的顺序进行的)。对基类而言(它采用了C++拆构函数的形式),你应该先进行派生类的清理,再进行基类的清理。
多态方法在构造函数中的行为
如果你在构造函数里面调用了动态绑定的方法,那么它会使用那个覆写后的版本。
一个好的构造函数应该,“用最少的工作量把对象的状态设置好,而且要尽可能地避免去调用方法。”构造函数唯一能安全调用的方法,就是基类的final方法。(这一条也适合private方法,因为它自动就是final的。)它们不会被覆写,因此也不会产生这种意外的行为。
用继承来进行设计
使用继承来表示行为的不同,而用成员数据来表示不同的状态。
纯继承与扩展
下传与运行时的类型鉴别
总结
练习
「读书笔记」Thinking in Java 3rd Edition - 8: Interfaces & Inner Classes