Java用Class對象實現自己的RTTI功能——即便我們要做的只是象造型那樣的一些工作。Class類也提供了其他大量方式,以方便我們使用RTTI。
首先必須獲得指向適當Class對象的的一個句柄。就象前例演示的那樣,一個辦法是用一個字串以及Class.forName()方法。這是非常方便的,因為不需要那種類型的一個對象來獲取Class句柄。然而,對於自己感興趣的類型,如果已有了它的一個對象,那麼為了取得Class句柄,可調用屬於Object根類一部分的一個方法:getClass()。它的作用是返回一個特定的Class句柄,用來表示對象的實際類型。Class提供了幾個有趣且較為有用的方法,從下例即可看出:
//: ToyTest.java // Testing class Class interface HasBatteries {} interface Waterproof {} interface ShootsThings {} class Toy { // Comment out the following default // constructor to see // NoSuchMethodError from (*1*) Toy() {} Toy(int i) {} } class FancyToy extends Toy implements HasBatteries, Waterproof, ShootsThings { FancyToy() { super(1); } } public class ToyTest { public static void main(String[] args) { Class c = null; try { c = Class.forName("FancyToy"); } catch(ClassNotFoundException e) {} printInfo(c); Class[] faces = c.getInterfaces(); for(int i = 0; i < faces.length; i++) printInfo(faces[i]); Class cy = c.getSuperclass(); Object o = null; try { // Requires default constructor: o = cy.newInstance(); // (*1*) } catch(InstantiationException e) {} catch(IllegalAccessException e) {} printInfo(o.getClass()); } static void printInfo(Class cc) { System.out.println( "Class name: " + cc.getName() + " is interface? [" + cc.isInterface() + "]"); } } ///:~
從中可以看出,class FancyToy相當復雜,因為它從Toy中繼承,並實現了HasBatteries,Waterproof以及ShootsThings的接口。在main()中創建了一個Class句柄,並用位於相應try塊內的forName()初始化成FancyToy。
Class.getInterfaces方法會返回Class對象的一個數組,用於表示包含在Class對象內的接口。
若有一個Class對象,也可以用getSuperclass()查詢該對象的直接基礎類是什麼。當然,這種做會返回一個Class句柄,可用它作進一步的查詢。這意味著在運行期的時候,完全有機會調查到對象的完整層次結構。
若從表面看,Class的newInstance()方法似乎是克隆(clone())一個對象的另一種手段。但兩者是有區別的。利用newInstance(),我們可在沒有現成對象供“克隆”的情況下新建一個對象。就象上面的程序演示的那樣,當時沒有Toy對象,只有cy——即y的Class對象的一個句柄。利用它可以實現“虛擬構建器”。換言之,我們表達:“盡管我不知道你的准確類型是什麼,但請你無論如何都正確地創建自己。”在上述例子中,cy只是一個Class句柄,編譯期間並不知道進一步的類型信息。一旦新建了一個實例後,可以得到Object句柄。但那個句柄指向一個Toy對象。當然,如果要將除Object能夠接收的其他任何消息發出去,首先必須進行一些調查研究,再進行造型。除此以外,用newInstance()創建的類必須有一個默認構建器。沒有辦法用newInstance()創建擁有非默認構建器的對象,所以在Java 1.0中可能存在一些限制。然而,Java 1.1的“反射”API(下一節討論)卻允許我們動態地使用類裡的任何構建器。
程序中的最後一個方法是printInfo(),它取得一個Class句柄,通過getName()獲得它的名字,並用interface()調查它是不是一個接口。
該程序的輸出如下:
Class name: FancyToy is interface? [false] Class name: HasBatteries is interface? [true] Class name: Waterproof is interface? [true] Class name: ShootsThings is interface? [true] Class name: Toy is interface? [false]
所以利用Class對象,我們幾乎能將一個對象的祖宗十八代都調查出來。