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