2010-05-02 77 views

回答

15

讓我們假設一個時刻,你可以做你的描述:

class B extends A { ... } 

Collection<A> collecA; 
List<B> listB; 

collecA = listB; // normally an error, but lets pretend its allowed 

collecA.add(new A()); // PROBLEM! 

因爲collecA是一家集 持有A S中的方法調用collecA.add(new A())似乎還好。但是,如果上述任務被允許,那麼我們有 的問題,因爲collecA實際上是參考List<B>實例 - 我只是 添加一個A到只能容納B s!

提問者還表示:

我不明白爲什麼,因爲收藏是通過列表來實現。

集合是List的超類無關緊要。即使您使用了兩個列表,此分配也是非法的。

class B extends A { ... } 
List<A> listA; 
List<B> listB; 
listA = listB; // still an error, still leads to the same problem 

的關鍵是,在List<A>可變只能引用List s表示可容納A秒。但是,實例不能容納As。因此,List<A>變量listA不能被指定爲參考listB所指的List<B>實例

或者更一般地講:B是的A一個子類做了暗示SomeGenericClass<B>SomeGenericClass<A>JLS §4.10的子類:亞型不通過泛型類型延伸:T <: U並不意味着C<T> <: C<U>。 )


這是從Java泛型教程這個例子/類比,幫助我理解這一點:

http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html

「理解爲什麼變得容易得多,如果你認爲有形物體的 - 東西,你可以真正的圖片 - 如鳥籠,

// A cage is a collection of things, with bars to keep them in. 
interface Cage<E> extends Collection<E>; 
... 
Cage<Lion> lionCage = ...; 
Cage<Butterfly> butterflyCage = ...; 

但是「動物籠子」呢?英語是模糊的,所以要精確假設我們正在談論的「所有的動物籠」

Cage<Animal> animalCage = ...; 

這是設計容納各種動物,混合在一起的籠子。它必須有足夠堅固的酒吧,以保持在獅子,足夠的間隔,以保持在蝴蝶。
...
由於獅子是一種動物(獅子是動物的一種亞型),問題就變成了:「獅子籠是一種動物籠子嗎?是Cage<Lion>Cage<Animal>的子類型嗎?」。通過動物籠的上述定義,答案必須是「否」。這是令人驚訝的!但是當你考慮它時,它是完全有意義的:一個獅子籠不能被假定爲保持在蝴蝶中,並且一個蝴蝶籠不能被假定爲擁有獅子。因此,無論籠可考慮「所有動物」鳥籠。

animalCage = lionCage; // compile-time error 
animalCage = butterflyCage; // compile-time error 

+3

好易理解的解釋伯特我一直讚賞愚蠢試圖瞭解泛型和繼承類比時,應Kartoch檢查一下 – tgai 2010-05-02 21:34:35

+2

@Tamon - 很高興你(希望其他人)發現它很容易理解,我對理解泛型和繼承有同樣的問題(更不用說通用通配符),所以它採用了一個新的例子,就像Java泛型教程在我的腦海中將它融入其中我搜索了'java generic cast animal cage'來記住我在哪裏閱讀這個比喻。:-) – 2010-05-02 22:16:41

+0

確實很好的解釋... – Kartoch 2010-05-03 10:39:06

5

Java泛型不是covariant

查看Java Theory and Practice: Generics Gotchas瞭解更多詳情。

的頁面顯示了一個簡單的例子,將嚴重破壞類型系統,如果它是協:

想象一下,你可以一個列表<整數>分配給一個列表<號碼>。然後將下面的代碼將讓你把東西,是不是一個Integer放入名單<整數>:

List<Integer> li = new ArrayList<Integer>(); 
List<Number> ln = li; // illegal 
ln.add(new Float(3.1415)); // ERROR: Adds a float to li, which is a list of Integers! 
8
Collection<? extends A> collecA 

這修復它。問題不是List extends Collection,而是泛型類型。

1

您可以指定清單< B>收集< B>,但不是列表< B>收集< A>。

試想一下,如果這是可能會發生什麼:

List<B> = new ArrayList<B>(); 
Collection<A> collecA = listB; //Assume that this line compiles 
collecA.add(new A()); 
B item = listB.get(0); //ClassCastException! 

正如你看到的,我們「上當」的泛型類型的系統,通過增加具體類型的實例,這應該只是有一個集合類型B(或後代)的對象。 因此,對B執行隱式轉換的最後一行失敗,並帶有ClassCastException。它出什麼問題了?編譯器不能保證類型安全,這是違反Java泛型原則之一的。

因此,已決定列表< B>是類別< B>,而不是列出< A>(或集合< A>)。

作爲一個側面評論,有趣的是,數組並沒有遵循相同的規則:String [] 一個Object [],並且賦值是合法的。