2014-03-03 41 views
22

考慮以下方法doSomething(List<Object>),它接受List<Object>作爲參數。爲什麼列表<String>不被接受爲列表<Object>?

private void doSomething(List<Object> list) { 
    // do something 
} 

現在考慮下面這些試圖調用doSomething(),我試圖通過List<String>doSomething()

List<Object> objectList; 
List<String> stringList; 

doSomething(stringList); // compilation error incompatible types 
doSomething(objectList); // works fine 

即使下面的代碼的代碼片段拋出編譯錯誤

objectList = stringList; // compilation error incompatible types 

我的問題是,爲什麼List<String>不能傳遞給接受List<Object>的方法?

+3

看看這個:http://docs.oracle.com/javase/tutorial/extra/generics/subtype.html –

+6

'名單<?擴展Object>'應該允許它工作 – AbstractChaos

+5

@AbstractChaos true,只要「做某事」不涉及「List#add(...)' – Marco13

回答

8

Java中的這個通用問題可能會讓任何不熟悉泛型的人感到困惑,因爲在第一眼看來,它看起來像字符串是對象,因此List<String>可用於需要List<Object>的地方,但這不是真實的。這會導致編譯錯誤。

如果你走一步,因爲List<Object>可以存儲任何包括StringInteger等,但List<String>只能存儲Strings這有一定道理。

另外看看:Why not inherit from List<T>?

+3

被選爲答案,因爲它讓我更好地理解。 – user3374518

+5

問題並不在於被調用者可以存儲任何東西:真正的問題是,如果允許,調用者不能確定是否有String實例,因此必須像在非泛型日期中那樣處理它(instanceof運營商)。 – robermann

31

因爲儘管String延伸ObjectList<String>不延伸List<Object>

更新:
一般來說,如果Foo是一個亞型(子類或子接口) BarG是一些泛型類型聲明,但並不是G<Foo>G<Bar>的子類型。

這是因爲集合確實發生了變化。在你的情況,如果List<String>是的List<Object>,然後亞型種類比String可以添加到它時,列表使用它的超類型引用如下其它:

List<String> stringList = new ArrayList<String>; 
List<Object> objectList = stringList;// this does compile only if List<String> where subtypes of List<Object> 
objectList.add(new Object()); 
String s = stringList.get(0);// attempt to assign an Object to a String :O 

和Java編譯器,以防止這些情況。

關於this的更多細節Java教程頁面。

+0

簡單準確。 +1 –

+1

+1;無論如何,你可以打電話給doSomething(((列表)(列表)stringList)) – robermann

+4

雖然這個答案是真的,它看起來更像是OP問題的確認。恕我直言,它缺乏最重要的部分:「爲什麼'列表'不是'列表'?」的子類型。 – Pshemo

15

你可以把一個錯誤類型的對象到列表IF這工作:

private void doSomething(List<Object> list) { 
    list.add(new Integer(123)); // Should be fine, it's an object 
} 

List<String> stringList = new ArrayList<String>(); 
doSomething(stringList); // If this worked.... 
String s = stringList.get(0); // ... you'd receive a ClassCastException here 
+0

+1,也許這裏是最好的例子:) – robermann

+2

PS:與泛型的超類型/子類型關係在http:// www。 angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ207 – Marco13

0

如果你不知道它會在你有什麼數據類型可以使用泛型在Java中如下

public static void doSomething(List<?> data) { 

} 

public static void main(String [] args) { 
    List<Object> objectList = new ArrayList<Object>(); 
    List<String> stringList = new ArrayList<String>(); 
    doSomething(objectList); 
    doSomething(stringList); 
} 

但在使用中的數據,你會被要求指定正確的數據類型作爲演員

+0

與問題沒有關係,我認爲 –

+0

雅,我知道與問題沒有關係,但既然您已經在問題上添加了適當的評論,認爲這將是很好的可能的共享在Java中使用泛型的通用方法: - / – rsakhale

3

一個類型,是舒美特imes預計List<Object>將是超類型的List<String>,因爲ObjectString的超類型。

Object[]String[]超類型,因爲ObjectString的超類型:

這種期望的事實,存在對陣列這樣的類型的關係的莖。 (這種類型的關係稱爲協方差。)

組件類型的超子類型關係擴展到相應的數組類型中。

對於泛型類型的實例,不存在這樣的類型關係。 (參數化類型不是協變的。)

檢查here更多細節

2

從泛型的Java教程:

測試你的泛型的理解。以下代碼段合法嗎?

List<String> ls = new ArrayList<String>(); // 1 
List<Object> lo = ls; // 2 

第1行肯定是合法的。問題的棘手部分是第2行。這可歸結爲以下問題:是字符串列表對象列表。大多數人本能地回答,「當然!」

好了,看看接下來的幾行:

lo.add(new Object()); // 3 
String s = ls.get(0); // 4: Attempts to assign an Object to a String! 

這裏我們已經走樣LS和LO。通過別名lo訪問ls(一個String列表),我們可以在其中插入任意對象。因此,ls不再持有字符串,當我們試圖從中得到某些東西時,我們會得到一個粗暴的驚喜。

Java編譯器當然會阻止這種情況發生。第2行將導致編譯時錯誤。

來源:Generics and Subtyping

4

之所以這些限制有差異的考慮的事情。

看看下面的代碼:

public void doSomething(List<Object> objects) 
{ 
    objects.add(new Object()); 
} 

擴大你的榜樣,你可以嘗試做到以下幾點:

List<String> strings = new ArrayList<String>(); 
string.add("S1"); 

doSomething(strings); 

for (String s : strings) 
{ 
    System.out.println(s.length); 
} 

希望這是顯而易見的,爲什麼這將打破,如果編譯器允許該碼被編譯(它沒有) - 在嘗試將Object轉換爲String時,列表中的第二項會出現ClassCastException

爲了能夠通過廣義集合類型,你需要這樣做:

public void doSomething(List<?> objects) 
{ 
    for (Object obj : objects) 
    { 
    System.out.println(obj.toString); 
    } 
} 

同樣,編譯器都在注視着你的背部和你分別用objects.add(new Object())更換System.out編譯器不會允許這種因爲objects可能已創建爲List<String>

有關差異的更多背​​景看維基百科ARTICAL Covariance and contravariance

相關問題