public class Type1 {
public static void main(String[] args) {
String s = null;
System.out.println(s instanceof String);
}
}
public class Type2 {
public static void main(String[] args) {
System.out.println(new Type2() instanceof String);
}
}
public class Type3 {
public static void main(String args[]) {
Type3 t3 = (Type3) new Object();
}
}
第一個程序,Type1,展示了instanceof操作符應(yīng)用于一個空對象引用時的行為。盡管null對于每一個引用類型來說都是其子類型,但是instanceof操作符被定義為在其左操作數(shù)為null時返回false。因此,Type1將打印false。這被證明是實踐中非常有用的行為。如果instanceof告訴你一個對象引用是某個特定類型的實例,那么你就可以將其轉(zhuǎn)型為該類型,并調(diào)用該類型的方法,而不用擔(dān)心會拋出ClassCastException或NullPointerException異常。
第二個程序,Type2,展示了instanceof操作符在測試一個類的實例,以查看它是否是某個不相關(guān)的類的實例時所表現(xiàn)出來的行為。你可能會期望該程序打印出false。畢竟,Type2的實例不是String的實例,因此該測試應(yīng)該失敗,對嗎?不,instanceof測試在編譯時刻就失敗了,我們只能得到下面這樣的出錯消息:
Type2.java:3: inconvertible types
found : Type2, required: java.lang.String
System.out.println(new Type2() instanceof String);
^
該程序編譯失敗是因為instanceof操作符有這樣的要求:如果兩個操作數(shù)的類型都是類,其中一個必須是另一個的子類型[JLS 15.20.2, 15.16, 5.5]。Type2和String彼此都不是對方的子類型,所以instanceof測試將導(dǎo)致編譯期錯誤。這個錯誤有助于讓你警惕instanceof測試,它們可能并沒有去做你希望它們做的事情。
第三個程序,Type3,展示了當(dāng)要被轉(zhuǎn)型的表達(dá)式的靜態(tài)類型是轉(zhuǎn)型類型的超類時,轉(zhuǎn)型操作符的行為。與instanceof操作相同,如果在一個轉(zhuǎn)型操作中的兩種類型都是類,那么其中一個必須是另一個的子類型。盡管對我們來說,這個轉(zhuǎn)型很顯然會失敗,但是類型系統(tǒng)還沒有強(qiáng)大到能夠洞悉表達(dá)式new Object()的運(yùn)行期類型不可能是Type3的一個子類型。因此,該程序?qū)⒃谶\(yùn)行期拋出ClassCastException異常。這有一點違背直覺:第二個程序完全具有實際意義,但是卻不能編譯;而這個程序沒有任何實際意義,但是卻可以編譯。
總之,第一個程序展示了instanceof運(yùn)行期行為的一個很有用的冷僻案例。第二個程序展示了其編譯期行為的一個很有用的冷僻案例。第三個程序展示了轉(zhuǎn)型操作符的行為的一個冷僻案例,在此案例中,編譯器并不能將你從你所做荒唐的事中搭救出來,只能靠VM在運(yùn)行期來幫你繃緊這根弦。