請考慮下面這個熟悉的類結構例子,它利用了多形性。常規類型是Shape類,而特別衍生出來的類型是Circle,Square和Triangle。
這是一個典型的類結構示意圖,基礎類位於頂部,衍生類向下延展。面向對象編程的基本目標是用大量代碼控制基礎類型(這裡是Shape)的句柄,所以假如決定添加一個新類(比如Rhomboid,從Shape衍生),從而對程序進行擴展,那麼不會影響到原來的代碼。在這個例子中,Shape接口中的動態綁定方法是draw(),所以客戶程序員要做的是通過一個普通Shape句柄調用draw()。draw()在所有衍生類裡都會被覆蓋。而且由於它是一個動態綁定方法,所以即使通過一個普通的Shape句柄調用它,也有表現出正確的行為。這正是多形性的作用。
所以,我們一般創建一個特定的對象(Circle,Square,或者Triangle),把它上溯造型到一個Shape(忽略對象的特殊類型),以後便在程序的剩余部分使用匿名Shape句柄。
作為對多形性和上溯造型的一個簡要回顧,可以象下面這樣為上述例子編碼(若執行這個程序時出現困難,請參考第3章3.1.2小節“賦值”):
//: Shapes.java package c11; import java.util.*; interface Shape { void draw(); } class Circle implements Shape { public void draw() { System.out.println("Circle.draw()"); } } class Square implements Shape { public void draw() { System.out.println("Square.draw()"); } } class Triangle implements Shape { public void draw() { System.out.println("Triangle.draw()"); } } public class Shapes { public static void main(String[] args) { Vector s = new Vector(); s.addElement(new Circle()); s.addElement(new Square()); s.addElement(new Triangle()); Enumeration e = s.elements(); while(e.hasMoreElements()) ((Shape)e.nextElement()).draw(); } } ///:~
基礎類可編碼成一個interface(接口)、一個abstract(抽象)類或者一個普通類。由於Shape沒有真正的成員(亦即有定義的成員),而且並不在意我們創建了一個純粹的Shape對象,所以最適合和最靈活的表達方式便是用一個接口。而且由於不必設置所有那些abstract關鍵字,所以整個代碼也顯得更為清爽。
每個衍生類都覆蓋了基礎類draw方法,所以具有不同的行為。在main()中創建了特定類型的Shape,然後將其添加到一個Vector。這裡正是上溯造型發生的地方,因為Vector只容納了對象。由於Java中的所有東西(除基本數據類型外)都是對象,所以Vector也能容納Shape對象。但在上溯造型至Object的過程中,任何特殊的信息都會丟失,其中甚至包括對象是幾何形狀這一事實。對Vector來說,它們只是Object。
用nextElement()將一個元素從Vector提取出來的時候,情況變得稍微有些復雜。由於Vector只容納Object,所以nextElement()會自然地產生一個Object句柄。但我們知道它實際是個Shape句柄,而且希望將Shape消息發給那個對象。所以需要用傳統的"(Shape)"方式造型成一個Shape。這是RTTI最基本的形式,因為在Java中,所有造型都會在運行期間得到檢查,以確保其正確性。那正是RTTI的意義所在:在運行期,對象的類型會得到鑒定。
在目前這種情況下,RTTI造型只實現了一部分:Object造型成Shape,而不是造型成Circle,Square或者Triangle。那是由於我們目前能夠肯定的唯一事實就是Vector裡充斥著幾何形狀,而不知它們的具體類別。在編譯期間,我們肯定的依據是我們自己的規則;而在編譯期間,卻是通過造型來肯定這一點。
現在的局面會由多形性控制,而且會為Shape調用適當的方法,以便判斷句柄到底是提供Circle,Square,還是提供給Triangle。而且在一般情況下,必須保證采用多形性方案。因為我們希望自己的代碼盡可能少知道一些與對象的具體類型有關的情況,只將注意力放在某一類對象(這裡是Shape)的常規信息上。只有這樣,我們的代碼才更易實現、理解以及修改。所以說多形性是面向對象程序設計的一個常規目標。
然而,若碰到一個特殊的程序設計問題,只有在知道常規句柄的確切類型後,才能最容易地解決這個問題,這個時候又該怎麼辦呢?舉個例子來說,我們有時候想讓自己的用戶將某一具體類型的幾何形狀(如三角形)全都變成紫色,以便突出顯示它們,並快速找出這一類型的所有形狀。此時便要用到RTTI技術,用它查詢某個Shape句柄引用的准確類型是什麼。