多态
多态(也称作动态绑定、后期绑定或运行时绑定)通过分离做什么和怎么做,从另一角度将接口和实现分离开来。多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序——即无论在项目最初创建时还是在需要添加新功能时都可以“生长”的程序。
8.1再论向上转型
把某个对象的引用视为对其基类型的引用的做法被称作向上转型。
8.2转机
将一个方法调用同一个方法主体关联起来被称作绑定。
后期绑定指在运行时根据对象的类型进行绑定。
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。
当发送消息给某个对象,该对象会断定应该做什么事(产生正确的行为)。
多态的特性:我们所做的代码修改,不会对程序中其他不应受到影响的部分产生破坏。换句话说,多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术。
8.3构造器和多态
- 复杂对象构造器调用顺序:
1、调用基类构造器
2、按声明顺序调用成员的初始化方法
3、调用导出类构造器的主体 - 在清理一个对象时,必须先对导出类进行清理,然后才是基类。这是因为导出类的清理可能会调用基类中的某些方法,所以需要使基类中的构件仍器作用而不应过早地销毁它们。
- 如果在一个构造器内部调用正在构造的对象的某个动态绑定方法,将可能会招致灾难:如果构造器只是在构建对象过程中的一个步骤,并且该对象所属的类时从这个构造器所属的类导出的,那么导出部分在当前构造器正在被调用的时刻仍旧是没有初始化的。然而一个动态绑定的方法调用却会向外深入到继承层次结构内部,它可以调用导出类里的方法。而该方法所操纵的成员可能还未进行初始化。
示例:
class Glyph{
void draw(){print("Glyph.draw()");}
Gpyph(){draw();}
}
class RoundGlyph extends Glyph{
private int radius = 1;
RoundGlyph(int r){radius=r;}
void draw(){print("RoundGlyph.draw(),radius="+radius);}
}
public class PolyConstructors{
public static void main(String[] args){ new RoundGlyph(5);}
}
输出结果为"RoundGlyph.draw(), radius=0"。其中radius不是初始值1,而是0。
8.4协变返回类型
表示在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型。
class Grain{
public String toString(){return "Grain";}
}
class Wheat extends Grain{
public String toString{return "Wheat";}
}
class Mill{
Grain process(){return new Grain();}
}
class WheatMill extends Mill{
Wheat process(){return new Wheat();}
}
8.5用继承进行设计
在使用现成类建立新类时,应首先考虑组合。组合不会强制我们的程序设计进入继承的层次结构中。而且组合更加灵活,因为它可以动态选择类型;相反,继承在编译时就需要知道确切类型。
示例:
class Actor{//...}
class HappyActor extends Actor{//...}
class SadActor extends Actor{//..}
class Stage{
private Actor actor = new HappyActor();
public void change(){actor = new SadActor();}
public void performPlay(){actor.act();}
}
public class Transmogrify{
public static void main(String[] args){
Stage stage= new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
}
纯继承与扩展: is-a 与 is-like-a
is-like-a:导出类有着相同的基本接口,但是它还具有由额外方法实现的其他特性。缺点:导出类中接口的扩展部分不能被基类访问,因此,一旦向上转型,就不能调用那些新方法。
向上转型会丢失具体的类型信息,向下转型能够或缺类型信息。在Java中,所有转型都会得到检查。
在运行期间对类型进行检查的行为称作“运行时类型识别”RTTI
class Useful{
public void f(){}
}
class MoreUseful{
public void f(){}
public void u(){}
}
public class RTTI{
public static void main(String[] args){
Useful[] x = {new Useful(), new MoreUseful()};
x[0].f();
x[1].f();
//x[1].u(); 编译错误
((MoreUseful)x[1]).u(); //向下转型
((MoreUseful)x[0]).u();//抛出异常
}
}
总结
多态意味着“不同的形式”。在面向对象的程序设计中,我们持有从基类继承而来的相同接口,以及使用该接口的不同形式:不同版本的动态绑定方法。