对象导论笔记-第三天 伴随多态的可互换对象

08/19/2011

在处理类型的层次结构时,经常想把一个对象不当作它所属的特定类型来对待,而是将其当作基类的对象来对待。这使得人们可以编写出不依赖于特定类型的代码。基类方法的操作都是泛化(generic)的。

这样的代码是不会受添加新类型影响的,而且添加心类型是扩展一个面向对象程序以便处理新情况的最常用方式。这种能力可以极大地改善我们的设计,同时也降低软件维护的代价

泛化的关键在于:当发送泛化方法的消息时,程序员并不想知道是哪一段代码将被执行。

如果不需要知道哪一段代码会被执行,那么当添加心的子类型时,不需要更改调用它的方法,它就能够执行不同的代码。但是问题是编译器uu发精确地了解哪一段代码将会执行,那么它该怎么办呢?

这个问题的答案,也是面向对象程序设计的最重要的妙诀:编译器不可能产生传统意义上的函数调用。为了解决这个问题,面向对象程序设计语言使用了后期绑定的概念。当想对象发送消息时,被调用的代码知道运行时才能确定。

为了执行后期绑定,Java使用一小段特殊的代码来替代绝对地址调用。这段代码使用在对象中存储的信息来计算方法体的地址。Java中,动态绑定是默认行为,不需要添加额外的关键字来实现多态。

下面通过几何形状(Shape)的例子来说明:

void doSomething(Shape shape) {
	shape.erase();
	//...
	shape.draw();
}

这个方法可以与任何Shape对话,因此他是独立于任何要绘制和擦除的对象的具体类型的。

Circel circle = new Circle();
Triangle triangle = new Triangle();
Line line = new Line();
doSomething(circle);
doSomething(triangle);
doSomething(line);

对doSomething的调用会自动地正确处理,而不用管对象的确切类型。当Circle被传入到语气接收Shape的方法中,究竟会发生什么?由于Circle可以被doSomething()看作是Shape,也就是说,doSomething()可以发送给Shape的任何消息,Circle都可以接收,那么,这么做就是完全安全且合乎逻辑的。

doSomething()的代码给人印象深刻之处在于,不知何故,它总是做了该做的。调用Circle的draw()方法所执行的代码与调用Square或是Line的draw()方法所执行的代码是不同的。正是多态才使得事情总是能够被正确处理。编译器和运行系统会处理相关的细节,你需要马上知道的只是事情会发生,更重要的是怎样通过利用它来进行设计。