這個程序看起來也許顯得有些奇怪。為什麼所有人都應該有意忘記一個對象的類型呢?進行上溯造型時,就可能產生這方面的疑惑。而且如果讓tune()簡單地取得一個Wind句柄,將其作為自己的自變量使用,似乎會更加簡單、直觀得多。但要注意:假如那樣做,就需為系統內Instrument的每種類型寫一個全新的tune()。假設按照前面的推論,加入Stringed(弦樂)和Brass(銅管)這兩種Instrument(樂器):
//: Music2.java
// Overloading instead of upcasting
class Note2 {
private int value;
private Note2(int val) { value = val; }
public static final Note2
middleC = new Note2(0),
cSharp = new Note2(1),
cFlat = new Note2(2);
} // Etc.
class Instrument2 {
public void play(Note2 n) {
System.out.println("Instrument2.play()");
}
}
class Wind2 extends Instrument2 {
public void play(Note2 n) {
System.out.println("Wind2.play()");
}
}
class Stringed2 extends Instrument2 {
public void play(Note2 n) {
System.out.println("Stringed2.play()");
}
}
class Brass2 extends Instrument2 {
public void play(Note2 n) {
System.out.println("Brass2.play()");
}
}
public class Music2 {
public static void tune(Wind2 i) {
i.play(Note2.middleC);
}
public static void tune(Stringed2 i) {
i.play(Note2.middleC);
}
public static void tune(Brass2 i) {
i.play(Note2.middleC);
}
public static void main(String[] args) {
Wind2 flute = new Wind2();
Stringed2 violin = new Stringed2();
Brass2 frenchHorn = new Brass2();
tune(flute); // No upcasting
tune(violin);
tune(frenchHorn);
}
} ///:~
這樣做當然行得通,但卻存在一個極大的弊端:必須為每種新增的Instrument2類編寫與類緊密相關的方法。這意味著第一次就要求多得多的編程量。以後,假如想添加一個象tune()那樣的新方法或者為Instrument添加一個新類型,仍然需要進行大量編碼工作。此外,即使忘記對自己的某個方法進行過載設置,編譯器也不會提示任何錯誤。這樣一來,類型的整個操作過程就顯得極難管理,有失控的危險。
但假如只寫一個方法,將基礎類作為自變量或參數使用,而不是使用那些特定的衍生類,豈不是會簡單得多?也就是說,如果我們能不顧衍生類,只讓自己的代碼與基礎類打交道,那麼省下的工作量將是難以估計的。
這正是“多形性”大顯身手的地方。然而,大多數程序員(特別是有程序化編程背景的)對於多形性的工作原理仍然顯得有些生疏。