Java编程思想1-对象导论

1.7伴随多态的可互换对象

在几何形例子中,方法操作都是泛化的形状,不关心它们是圆形,正方形,三角形什么未定义的形状。所有的几何形都可以被绘制,擦除和移动,所以这些方法都是直接一个几何形对象发送消息,它们不用担心对象将如何处理信息。这样的代码是不会受添加新类型影响的,而且添加新类型是扩展一个面向对象程序以便处理新情况的最常用方式。例如,可以从“几何形”中导出一个新的子类型“五角形”,而并不需要修改处理泛化几何形状的方法。通过导出新的子类型而轻松扩展设计的能力是对改动进行封装的基本方式之一。这种能力可以极大地改善我们的设计,同时也降低软件维护的代价。

但是,在试图将导出类型的对象当作其泛化基类型对象来看待时,(把圆形看作是几何形,把自行车看作交通工具,把鸬鹚看作是鸟等等),仍然存在一个问题。//问题1

如果某个方法要让泛化几何形状绘制自己,让泛化交通工具行驶,或者让泛化的鸟类移动,那么编译器在编译时是不可能知道应该执行哪一段代码的。

因此,编译器无法精确地了解哪一段代码将会被执行,那么它该怎么办?//问题2

例如Java编程思想1-对象导论那么,这是如何发生呢?//问题3

这个问题答案:编译器不可能产生传统意义上的函数调用。一个非面对对象编程的编译器产生的函数调用会引起所谓的前期绑定,这么做意味着编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将要执行的代码的绝对地址。然而在OOP中,程序直到运行时才能够确定代码的地址,所以当消息发送到一个泛化对象时,必须采用其他的机制。//问题4

为解决这个问题,面向对象程序设计语言使用了后期绑定的概念。

当向对象发送消息时,被调用的代码直到运行时才能确定。编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查(无法提供此类保证的语言被称为弱类型的),但是并不知道将被执行的确切代码。

例:

void doSomething(Shape shape) {

    shape.erase();

    //...

    shape.draw();

}

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

如果程序中其他部分用的doSomething()方法,

Circle circle = new Circle();

Triangle triangle = new Triangle();

doSomething(circle);

doSomething(triangle);

doSomething(line);

对于doSomething()的调用会自动地正确处理,而不管对象的确切类型。

当Circle被传入预期接受Shape的方法中,究竟会发生什么?

由于Circle可以被doSomething()看作是Shape,也就是说,doSomething()可以发送给Shape的任何消息,Circle都可以接收,那么,这么做就是完全按去且符合逻辑了。

注意:这些代码并不是说“如何使Circle,请这样做;如果是Square,请这样做……”,如果编写了这样检查Shape所以实际可能类型的代码,那么这段代码肯定是杂乱不堪的,而且在每次添加了Shape 的新类型之后都要去修改这段代码。

这里所以表达的意思仅仅是“你是一个Shape,我知道你可以erase()和draw()你自己,那么去做吧,但是要注意细节的正确性”。

转载于:https://my.oschina.net/869088067/blog/714116