2 回答

TA貢獻(xiàn)1854條經(jīng)驗(yàn) 獲得超8個贊
你很接近。編譯時檢查所有結(jié)果:
a
是類型List
所以調(diào)用
a.add("test");
平底鍋。b
是(編譯時)類型ArrayList<Integer>
所以
b.get(0)
退房。請注意,僅針對變量的編譯時類型進(jìn)行檢查。當(dāng)編譯器看到a.add("test")
它不知道通過變量引用的對象的運(yùn)行時間值a
。一般來說,它真的不能(在理論計(jì)算機(jī)科學(xué)中有一個關(guān)于這個的結(jié)果),盡管控制流類型分析可以捕捉到很多這樣的事情。像 TypeScript 這樣的語言可以在編譯時做出驚人的事情。
現(xiàn)在您可能假設(shè)在運(yùn)行時可以檢查這些事情。唉,在 Java 中他們不能。Java 刪除了泛型類型。查找有關(guān) Java 類型擦除的文章以了解詳細(xì)信息。TL;DR 是List<Integer>
編譯時的 aList
在運(yùn)行時變?yōu)樵嫉摹VM 沒有辦法“具體化”泛型(盡管其他語言有?。┧援?dāng)引入泛型時,決定 Java 將刪除泛型類型。所以在運(yùn)行時,你的代碼沒有類型問題。
我們來看一下編譯后的代碼:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: astore_2
10: aload_2
11: ldc #4 // String test
13: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
22: aload_1
23: iconst_0
24: invokeinterface #7, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
29: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
32: return
在這里您可以直接看到?jīng)]有運(yùn)行時類型檢查。因此,對您的問題的完整(但看似輕率)的答案是 Java 僅在編譯時根據(jù)變量的類型(在編譯時已知)檢查類型,但泛型類型參數(shù)會被刪除,并且代碼在沒有它們的情況下運(yùn)行。

TA貢獻(xiàn)1789條經(jīng)驗(yàn) 獲得超8個贊
令人驚訝的是,b.get(0)
它沒有運(yùn)行時檢查。我們希望編譯器解釋的代碼具有以下含義:
System.out.println((Integer)b.get(0)); // throws CCE
事實(shí)上,如果我們要嘗試:
Integer str = b.get(0); // throws CCE
我們會得到一個運(yùn)行時ClassCastException
。
事實(shí)上,我們甚至?xí)玫较嗤腻e誤切換printf
代替println
:
System.out.printf(b.get(0)); // throws CCE
這有什么意義?
由于向后兼容,這是一個無法修復(fù)的錯誤。如果目標(biāo)上下文可以允許刪除檢查轉(zhuǎn)換,那么盡管更改了語義,它也會被忽略。在這種情況下,過載從println(Integer)
變?yōu)?code>println(Object)。比這更糟糕的是,有一個println(char[])
具有不同行為的過載!
無論如何,不要使用原始類型或稀有類型,不要重載來改變行為(如果你可以管理它,也不要重載),并在將優(yōu)化提交到無法修復(fù)的規(guī)范之前要非常小心。
添加回答
舉報(bào)