2010-12-18 187 views
4

我有一個關於Java泛型和集合的問題。它被認爲是很好的做法,申報的集合是這樣的:Java泛型和集合

List<String> catNames = new ArrayList<String>(); 

,因爲你可以改變List的類型,而不必擔心破壞你的代碼的其餘部分。但是,當我嘗試這樣做:

private static Map<IssueType, List<Issue>> orphanedAttrMap = new HashMap<IssueType, ArrayList<Issue>>(); 

javac抱怨

Type mismatch: cannot convert from HashMap<ResultsAggregator.IssueType,ArrayList<Issue>> to HashMap<ResultsAggregator.IssueType,List<Issue>> 

此外,這是完全合法的:

private static Map<IssueType, List<Issue>> orphanedAttrMap = new HashMap<IssueType, List<Issue>>(); 

這似乎更加混亂,因爲List是一個接口,不是具體的課程。這裏發生了什麼?這是一個類型擦除問題?

回答

8

如果是合法的編譯這樣的代碼,你就已經能夠在HashMap偷偷插入其他類型的元素:

HashMap<IssueType, List<Issue>> a = new HashMap<IssueType, ArrayList<Issue>>(); 
a.put(someIssue, new SomeClassThatImplementsListOfIssueButIsNotArrayList()); 

這是不是你所期望的。 ArrayList<String>List<String>,但這不足以讓此代碼安全正確。爲了安全起見,它還要求List<String>ArrayList<String>,這意味着泛型類型參數在這裏不是協變的。

你最後的代碼是合法的,因爲沒有要求類型參數是一個具體的類。同樣,沒有什麼要求字段是抽象類型的。

+0

但是,在第一種被認爲是「良好實踐」的情況下,情況是不是也如此? – 2010-12-18 04:20:11

+0

啊我明白了,那雖然很奇怪...... – 2010-12-18 04:21:10

+1

@Amir:不,它不是。第一行聲明一個'List '類型的變量。如果稍後將某個其他列表分配給該變量,則將完全替換該實例。 ArrayList中沒有改變內容。你無法用這條線做一些壞事。 – 2010-12-18 04:23:07

4

沒有理由在第二個示例中指定ArrayList。它實際上並沒有創建一個列表,所以最好把接口放在那裏。您稍後可以調用以下方法。

Map<IssueType, List<Issue>> orphanedAttrMap = new HashMap<IssueType, List<Issue>>(); 

orphanedAttrMap.put(IssueType.TYPE, new ArrayList<Issue>()); 
+0

這是更好的方法,還是更好地宣佈LHS爲ArrayList ? – 2010-12-18 04:19:33

+0

@Amir,這是更好的方法,除非使用此映射的代碼依賴ArrayList中不屬於List接口(例如,ensureCapacity())的某些內容。即使如此,您最好將其保留爲List,並使用if(... instanceof ...)和向下轉換來訪問真正需要的ArrayList方法。使用接口幾乎總是比使用具體類更好。 – 2010-12-18 07:45:18