2015-04-28 37 views
5

下面的代碼的結果:字符串添加到列表<Integer>

public class ListIntegerDemo1 { 

    public static void addToList(List list) {list.add("0067");list.add("bb");} 
    public static void main(String[] args) { 
     List<Integer> list = new ArrayList<>(); 
     addToList(list); 
     System.out.println(list.get(0)); 
    }  
} 

"0067"

我會期待一個RuntimeException或類似的,因爲我將一個字符串添加到整數列表。

它爲什麼沒有任何問題?

+1

請參閱:http://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens –

+0

在您的addToList方法中,您沒有指定List參數所採用的類型,所以更像'List ',你可以添加任何對象到列表中 – Arkantos

回答

9

在運行時,通用類型參數被擦除,所以List<Integer>變爲List,您可以添加任何引用類型。

如果將addToList(List list)更改爲addToList(List<Integer> list),則編譯器將阻止您在此方法中將字符串添加到該列表中。

+0

我們至少應該有一個編譯時錯誤。 –

+3

@SaurabhJhunjhunwala你會在這裏得到一個使用原始List類型的警告 - 'addToList(List list)' - 因爲你使用的是原始List類型,所以你不會遇到編譯錯誤。 – Eran

+1

我相信你的答案的第一行可能是更好的影響的第二行? – CKing

1

有兩個理由說明爲什麼不拋出異常,這取決於我們如何看待這個特定的例子。也取決於爲什麼,或者,我們認爲應該拋出異常應該。 (如果我們認爲應該拋出一個,因爲Java清楚地認爲是另外的)

即使我們知道爲什麼add不會拋出異常,因爲問題中的代碼很棘手,因爲一個原因與Java選擇重載的方式。我建議運行以下程序以更好地理解:

class Example { 
    static void m(int arg) { 
     System.out.println("int " + arg); 
    } 
    static void m(Object arg) { 
     System.out.println("Object " + arg); 
    } 

    public static void main(String[] args) { 
     m(new Integer(0)); 
    } 
} 

打印Object 0。爲什麼?由於Java在三個階段搜索過載:*

  1. 第一階段在不允許裝箱,取消裝箱和可變參數的情況下查找重載。
  2. 第二階段尋找允許裝箱和拆箱的重載。
  3. 第三階段尋找允許裝箱,取消裝箱和可變參數的重載。

這些階段是「短路」,所以在m(new Integer(0))的情況下,第1階段考慮int過載需要取消裝箱之前找到Object過載。由於發現過載,階段2未嘗試。

同樣的事情發生在ListIntegerDemo1,其中System.out.println(Object)被選中。

泛型是erased,這意味着:

  • list.get實際上返回Object
  • 要將其結果分配給Integer,編譯器會插入一個強制轉換,如(Integer) list.get(0)

但是不需要插入一個調用Object重載,所以一個異常不會拋出。

使用raw typeList添加StringList<Integer>被稱爲堆污染。問題中的代碼是堆污染如此糟糕的一個例子:因爲有時候我們沒有發現它。有時候我們會在很晚的時候發現它,並且不知道錯誤實際發生在哪裏。

異常,如果我們這樣做將會被拋出:

Integer i = list.get(0); 
System.out.println(i); 

因爲在這種情況下,投需要做作業。

並且如果我們這樣做:

public class ListIntegerDemo1 { 
    public static void addToList(List list) {list.add(67);list.add(0xbb);} 
    public static void main(String[] args) { 
     List<String> list = new ArrayList<>(); 
     addToList(list); 
     System.out.println(list.get(0)); 
    }  
} 

因爲在這種情況下,System.out.println(String)被選擇爲更加具體和鑄造插入。

我們也可以防止我們List<Integer>通過使用Collections#checkedList在不安全的代碼被污染:

List<Integer> list = Collections.checkedList(
    new ArrayList<>(), Integer.class); 

在這種情況下,一個異常會從裏面addToList調用add被拋出。

當然,最好的解決辦法是不使用原始類型:

static void addToList(List<String> list) { 
    list.add("0067"); 
    list.add("bb"); 
} 

現在我們不能通過一個List<Integer>的方法。

addToList也可以接受List<? super String>


*詳情請見15.12.2

相關問題