7.1 上溯造型
在第 6 章,大家已知道可将一个对象作为它自己的类型使用,或者作为它的
基础类型的一个对象使用。取得一个对象句柄,并将其作为基础类型句柄使用的
行为就叫作“上溯造型”——因为继承树的画法是基础类位于最上方。
但这样做也会遇到一个问题,如下例所示(若执行这个程序遇到麻烦,请参
考第 3 章的 3.1.2 小节“赋值”):
252-253 页程序
其 中 , 方 法 Music.tune() 接 收 一 个 Instrument 句 柄 , 同 时 也 接 收 从
Instrument 衍生出来的所有东西。当一个 Wind 句柄传递给 tune()的时候,就会
出现这种情况。此时没有造型的必要。这样做是可以接受的;Instrument 里的
接口必须存在于 Wind 中,因为 Wind 是从 Instrument 里继承得到的。从 Wind
向 Instrument 的上溯造型可能“缩小”那个接口,但不可能把它变得比
Instrument 的完整接口还要小。
7.1.1 为什么要上溯造型
这个程序看起来也许显得有些奇怪。为什么所有人都应该有意忘记一个对象
的类型呢?进行上溯造型时,就可能产生这方面的疑惑。而且如果让 tune()简
单地取得一个 Wind 句柄,将其作为自己的自变量使用,似乎会更加简单、直观
得多。但要注意:假如那样做,就需为系统内 Instrument 的每种类型写一个全
新的 tune()。假设按照前面的推论,加入 Stringed(弦乐)和 Brass(铜管)
这两种 Instrument(乐器):
253-254 页程序
这 样 做 当 然 行 得 通 , 但 却 存在 一 个 极 大 的 弊端 : 必 须 为 每 种新 增 的
Instrument2 类编写与类紧密相关的方法。这意味着第一次就要求多得多的编程
量。以后,假如想添加一个象 tune()那样的新方法或者为 Instrument 添加一个
新类型,仍然需要进行大量编码工作。此外,即使忘记对自己的某个方法进行过
载设置,编译器也不会提示任何错误。这样一来,类型的整个操作过程就显得极
难管理,有失控的危险。
但假如只写一个方法,将基础类作为自变量或参数使用,而不是使用那些特
定的衍生类,岂不是会简单得多?也就是说,如果我们能不顾衍生类,只让自己
的代码与基础类打交道,那么省下的工作量将是难以估计的。
这正是“多形性”大显身手的地方。然而,大多数程序员(特别是有程序化
编程背景的)对于多形性的工作原理仍然显得有些生疏。
7.2 深入理解
对于 Music.java 的困难性,可通过运行程序加以体会。输出是 Wind.play()。
这当然是我们希望的输出,但它看起来似乎并不愿按我们的希望行事。请观察一
下 tune()方法:
public static void tune(Instrument i) {
评论