有兩個理由說明爲什麼不拋出異常,這取決於我們如何看待這個特定的例子。也取決於爲什麼,或者,我們認爲應該拋出異常應該。 (如果我們認爲應該拋出一個,因爲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在三個階段搜索過載:*
- 第一階段在不允許裝箱,取消裝箱和可變參數的情況下查找重載。
- 第二階段尋找允許裝箱和拆箱的重載。
- 第三階段尋找允許裝箱,取消裝箱和可變參數的重載。
這些階段是「短路」,所以在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
添加String
到List<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。
請參閱:http://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens –
在您的addToList方法中,您沒有指定List參數所採用的類型,所以更像'List