這個程序看起來也許顯得有些奇怪。為什麼所有人都應該有意忘記一個對象的類型呢?進行上溯造型時,就可能產生這方面的疑惑。而且如果讓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添加一個新類型,仍然需要進行大量編碼工作。此外,即使忘記對自己的某個方法進行過載設置,編譯器也不會提示任何錯誤。這樣一來,類型的整個操作過程就顯得極難管理,有失控的危險。
但假如只寫一個方法,將基礎類作為自變量或參數使用,而不是使用那些特定的衍生類,豈不是會簡單得多?也就是說,如果我們能不顧衍生類,只讓自己的代碼與基礎類打交道,那麼省下的工作量將是難以估計的。
這正是“多形性”大顯身手的地方。然而,大多數程序員(特別是有程序化編程背景的)對於多形性的工作原理仍然顯得有些生疏。