2011-10-18 62 views
31

我正與通用和數組玩,看來下面的代碼編譯好,數組泛型列表

ArrayList<Key> a = new ArrayList<Key>(); 

但是,編譯器會抱怨這個,

ArrayList<Key>[] a = new ArrayList<Key>[10]; 

通過計算器讀後,我有點理解,這是由於類型擦除,我可以修復它通過使用,

ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10]; 

或列表列表

ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>(); 

但我無法弄清楚幕後的原因。特別是,爲什麼第二個是非法的,因爲第一個是非常好的。爲什麼編譯器不會抱怨列表的列表。

+3

http://stackoverflow.com/questions/217065/cannot-create-an-array-of-linkedlists-in-java – tcb

回答

22

您不能擁有數組,因爲數組需要原始類型。你在第二個實例中對它進行了類型轉換,這使它符合定義的類型,因此是合法的(但是,這是不可能的)。列表的列表是合法的,因爲ArrayList不是數組。

請閱讀official tutorial中的第7.3章(第15頁)以瞭解更多詳情。

陣列對象的組件類型可能不是一個類型變量或 參數化類型,除非它是一個(無界)通配符type.You可以 聲明數組類型,其元素類型是類型變量或 參數化類型,但不包含數組對象。當然,這很煩人。這種限制是必要的,以避免像情況:

List<String>[] lsa = new List<String>[10]; // not really allowed 
Object o = lsa; 
Object[] oa = (Object[]) o; 
List<Integer> li = new ArrayList<Integer>(); 
li.add(new Integer(3)); 
oa[1] = li; // unsound, but passes run time store check 
String s = lsa[1].get(0); // run-time error - ClassCastException 

如果參數化類型的陣列被允許,上面的例子中會 編譯沒有任何未檢查的警告,並在運行時仍然會失敗。

教程然後接着說以下內容:

由於在運行時不存在類型變量,沒有辦法確定 實際的數組類型是什麼。 解決這些種類的限制的方法是使用類文字的運行時間 型令牌

1

陣列允許逃脫類型檢查(如克里斯的回答所示)。所以,你可以有一個通過所有編譯器檢查的代碼(編譯器沒有「未經檢查」的警告),但是在運行時出現ClassCastException異常。 禁止這種構造會給開發人員帶來問題,所以會出現警告。

4

我自己有一個similar question - FWIW,我沒有找到答案有說服力。從最詳細的解答(指的PDF參考)中相關的章節是這樣的:

的組件類型的數組對象的可能不是一個類型變量或 參數化類型,除非它是一個(無界)通配符類型。您可以使用 聲明其元素類型爲類型變量或 參數化類型的數組類型,但不聲明數組對象。這很煩人,肯定是 。此限制是必要的,以避免像

 List<String>[] lsa = new List<String>[10]; // not really allowed 
     Object o = lsa; 
     Object[] oa = (Object[]) o; 
     List<Integer> li = new ArrayList<Integer>(); 
     li.add(new Integer(3)); 
     oa[1] = li; // unsound, but passes run time store check 
     String s = lsa[1].get(0); // run-time error - ClassCastException 

,因爲我可以貓情形名單[]爲Object [],然後推不正確的數據爲對象[],然後參考錯誤從List參考,通過鑄造裁判,這是不好的/不允許的?但只有新的?

對於我來說,如何用新的方法來說明這個問題或多或少是一個問題而不是用法,仍然橫跨我的眼睛盯着它,希望它開始有意義,或者在最少解析成一個漂亮的3D圖像。

+0

編譯器需要利用實用方法保證通用型安全;當它不能時,它必須禁止代碼或發出警告。在這個例子中,第二行的警告似乎就足夠了; java決定徹底禁止泛型數組的創建似乎太苛刻了。 – irreputable

+1

是'ArrayList [] a =(ArrayList [])new ArrayList [10]; '仍然有同樣的問題,但它更清楚地表明你正在欺騙類型系統(並且它會產生一個警告。) – finnw

+0

finnw,你的評論是我認爲正確的答案和唯一的方法,這對我有意義。 –

2

創建通用數組不是類型安全的(請參閱Joshua Bloch的「Effective Java - second edition」中的「項目25:首選列表到數組」)。

用途:

List<List<Key>> b = new ArrayList<List<Key>>(10); 

或用Java SE 7中:

List<List<Key>> b = new ArrayList<>(10); 
4

陣列是窮人的仿製藥;與真正的泛型,應該避免陣列,雖然並不總是可能的。數組是協變的,泛型是不變的;加上刪除,事情就不太好,正如克里斯的答案中的例子所示。

但是我認爲可以放寬規範以允許通用數組的創建 - 這裏真的沒有問題。上陣時危險到來;那時編譯器警告就足夠了。

其實Java確實爲可變參數方法創建了泛型數組,所以它有點虛僞。

這裏是採取的這一事實

@SafeVarargs 
static <E> E[] arrayLiteral(E... array) 
{ 
    return array; 
} 

@SafeVarargs 
static <E> E[] newArray(int length, E... array) 
{ 
    return Arrays.copyOf(array, length); 
} 

// usage 

    List<String>[] array1 = arrayLiteral(list, list); 

    List<String>[] array2 = newArray(10); 
+0

這可能是因爲後向兼容性。假設某個未來版本(Java 9?)允許創建通用數組,那麼當Java 9代碼與Java 5/6/7/8代碼混合(可以在不發出警告的情況下上傳數組)時,結果將是意外的ClassCastExceptions。 – finnw

+0

如果'E'是一個類型變量,這不起作用。當'E'是一個類型變量時,可變參數創建了一個'E'的擦除數組,與'(E [])new Object [n]'沒有多大區別。請參閱[http://ideone.com/T8xF91](http://ideone.com/T8xF91)。 – Radiodef