2012-03-27 48 views
6

在回答記者提問關於這裏:https://stackoverflow.com/a/9872630/82609爲什麼我們可以使用陣列的通用參考

我試着做到以下幾點:

Comparator<String>[] comparators = new Comparator[] {...}; 

它的工作原理!但是,下列情況不:

Comparator<String>[] comparators = new Comparator<String>[] {...}; 

論相關的問題,我的假設:

我想這是因爲最初的陣列的合同可能是一些 這樣的:

如果您創建了一個X類型的數組,那麼您將永遠無法將 中的任何東西放入IS-NOT-AN X中。如果您RY,你會得到一個ArrayStoreException信息

因此允許使用泛型創建陣列會導致像不同 規則:如果您創建X<Y>類型的數組

,你永遠不會能夠到 把任何不是一個X.如果你嘗試,你會得到一個 ArrayStoreException。但是,由於類型擦除,您可以添加X<Y>X<Z>對象!


但想着它,將它確實是一個問題有:

Comparator<String>[] comparators = new Comparator<String>[] {...}; 

我真的不明白爲什麼它是不可能的,因爲使用這樣的事情:

  • 檢查運行時插入的類
  • 檢查在編譯時

插入最後,我們可以使用與一般類型的參考和,因爲不可能創建一個泛型類型的數組的數組類型,我想很多人甚至不知道這是可能的。

我只是想知道是否有人知道背後的原因?

這是一個有點像強迫人們使用List<String> = new ArrayList();,而不是使用List<String> = new ArrayList<String>();


dimitrisli你給約書亞布洛赫的名著一個很好爲例。 正如你所解釋的那樣,使用通用數組+協方差都是危險的,並且可能導致ClassCastException,而我們期望使用協方差數組的ArrayStoreException。

但請注意以下幾點仍然是合法的,並導致相同:

List<String>[] stringLists = new List[1]; 
List<Integer> intList = Arrays.asList(42); 
Object[] objects = stringLists; 
objects[0] = intList; 
String s = stringLists[0].get(0); 

但是它產生在編譯時未檢查鑄警告,因爲你mentionned,在運行一個ClassCastException。

+0

可能重複的[錯誤通用數組創建](http://stackoverflow.com/questions/3903196/error-generic-array-creation) – 2012-03-27 09:16:25

+0

@DaveWebb什麼是重複的? – 2012-03-27 09:19:21

+0

有人已經問過同樣的問題。 http://stackoverflow.com/questions/3903196/error-generic-array-creation – 2012-03-27 09:24:45

回答

4

我明白你來自哪裏(實際意義上我基本同意),但我認爲有一個區別激發了目前的情況。

如上所述,擦除意味着通用參數在運行時不可用,因此在編譯時檢查類型(對於List<String>Comparator<String>[])。關鍵是,這是基於變量的通用參數。

另一方面,陣列在運行時檢查它們的參數類型,當它們被插入時,如果它們被誤用(通常是由於濫用它們的協變性),它們可以拋出ArrayStoreException。因此數組需要能夠在內部執行兩個子彈點檢查,當然他們不能在運行時檢查通用參數。因此,實例化泛型數組是沒有意義的,因爲數組必須完全忽略泛型參數,因爲這樣會導致誤導。

也就是說,將這樣的數組分配給參數化的引用確實有意義,因爲編譯器可以執行通用檢查。你認爲這涵蓋了所有的基礎,並確保檢查泛型類型(只要變量參數設置正確)。

這個選擇背後的原因,以及爲什麼數組在這方面不同於集合,是因爲數組在插入時需要實際檢查它們的參數類型,而集合只是把你的單詞用於它會允許類型錯誤在稍後進入ClassCastException

+0

謝謝,我想你指出了:問題可能是關於數組協變 – 2012-03-27 09:32:21

1

從大Effective Java Second Edition頁面引用120:

爲什麼通用陣列的創建是非法的 - 將無法編譯!

List<String>[] stringLists = new List<String>[1]; // (1) 
List<Integer> intList = Arrays.asList(42); // (2) 
Object[] objects = stringLists; // (3) 
objects[0] = intList; // (4) 
String s = stringLists[0].get(0); // (5) 

讓我們假設1號線,這將創建一個通用的陣列,是合法的。第2行創建並且 初始化包含單個元素的List<Integer>。第3行將 List<String>數組存儲到Object數組變量中,這是合法的,因爲數組 是協變的。線4個存儲List<Integer>到 對象陣列的鞋底元件,其成功,因爲泛型是通過擦除實現:和的 運行時類型一個List<String>[]實例是List[]一個List<Integer>實例的 運行時類型是簡單地列出,所以這分配不會生成 ArrayStoreException。現在我們遇到了麻煩。我們已經將一個List<Integer> 實例存儲到聲明爲僅保存List<String>實例的數組中。在第5行的 中,我們從此陣列中的唯一列表中檢索唯一元素。編譯器 自動將檢索到的元素轉換爲字符串,但它是一個整數,所以我們在運行時得到 a ClassCastException。爲了防止發生這種情況,第1行 (它創建一個通用數組)會生成編譯時錯誤。

+0

謝謝,很好的例子,我編輯我的問題。請注意,我們仍然可以使用列表 [] stringLists = new List [1];相反,它工作正常。你知道爲什麼允許嗎? – 2012-03-27 09:43:26

+1

這是一個類型安全警告,因爲我們嘗試使用原始簽名List []創建並將其分配到通用列表 []。儘管我們將其更改爲列表 [],但編譯器會抱怨以避免所描述的情況。 – dimitrisli 2012-03-27 10:13:34

相關問題