2011-06-28 62 views
6

我有以下類:另一個Java泛型的問題

interface Able{/* ... */} 
class A implements Able{/* ... */} 

,我有

Map<String,? extends Able> as; 
as = new HashMap<String, A>(); 

爲什麼下面導致錯誤:

as.put("a", new A()); 

任何想法?

+2

什麼錯誤是什麼呢? – Bohemian

回答

15

對java泛型的引用很好(jdk site)。

確實@Oli_Charlesworth給出了一個很好的答案,但也許這一個會更完整。

Collection<? extends Able>中,您不能插入任何正確的東西。

如果你有

class A implements Able {...} 

class B implement Able {...} 

然後,Collection<? extends Able>是一個超級型兩種:

Collection<A> 
Collection<B> 

因此它是合法的,寫這樣

一些聲明
//Code snippet 01 
Collection< ? extends Able > list; 
Collection<A> listA; 
Collection<B> listB; 
list = listA; 
list = listB; 

這確實是通配符號Collection<? extends Able>存在的原因。

但是,在這裏事情變得更有趣:

Collection<A>只能插入對象是A(包括子類)。 Collection<B>也一樣。在這兩個你不能添加的東西,只是Able。例如:

//Code snippet 02 
listA.add(new A()); //valid at compile-time 
listA.add(new B()); //not valid at compile-time 
listB.add(new B()); //valid at compile-time 
listB.add(new A()); //not valid at compile-time 

因此,如果組我們在code snippets 01 & 02看見,你就會明白,這是絕對不可能的編譯器接受像聲明:

Collection< ? extends Able > list; 
list.add(new A());   //not allowed, will work only if list is List<A> 
list.add(new B());   //not allowed, will work only if list is List<B> 

所以,是的,超類型Collection< ? extends Able >不接受添加任何內容。更通用的類型提供了子類型的功能交集,因此,子類型的功能更少。在這裏,我們失去了添加A對象和B對象的能力。這些功能將在隨後的層次結構發生......它甚至意味着我們不能在超類中添加任何東西Collection< ? extends Able >

附記:

另外,還要注意在Collection<Able>你可以添加任何你希望是這樣的:

Collection<Able> list; 
list.add(new A());   //valid 
list.add(new B());   //valid 

但是,Collection<Able>不是Collection<A>Collection<B>一個超類。與任何繼承關係一樣,這意味着子類可以做任何超類可以做的事情,因爲繼承是專業化的。所以,這意味着我們可以將A對象和B對象添加到Collection<A>Collection<B>的兩個子類,事實並非如此。所以,因爲它不是一個超你不能有:

Collection<Able> list; 
Collection<A> listA; 
Collection<B> listB; 
list = listA; //not valid because there is no inheritance hierarchy 
list = listB; //not valid because there is no inheritance hierarchy 

注意繼承是hyperonimic關係(泛化/特化)和集合定義meronimic關係(容器/ containee)。將兩者正式結合起來讓人頭痛,儘管人類模糊的生物很容易使用它,例如法語形容詞:synecdocque。 :)

+0

您的答案不僅僅是好的,爲什麼我不能將A的實例添加到集合<?擴展Able>,儘管A實際上是Able類型的...... 這裏的類型un-safety在哪裏? Thnx :) – elmorabea

+0
8

http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html

There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into [a wildcard-based container]. For instance, this is not allowed:

public void addRectangle(List<? extends Shape> shapes) { 
    shapes.add(0, new Rectangle()); // Compile-time error! 
} 

You should be able to figure out why the code above is disallowed. The type of the second parameter to shapes.add() is ? extends Shape -- an unknown subtype of Shape . Since we don't know what type it is, we don't know if it is a supertype of Rectangle ; it might or might not be such a supertype, so it isn't safe to pass a Rectangle there.

0

您無法插入任何類型的任何對象集合中使用通配符聲明「?」

一旦你定義一個集合作爲列表只能插入「空」

,編譯器無法知道它的安全添加SubAble。

如果Collection<SubSubAble>已被分配給Collection<Able>會怎麼樣?這將是一個有效的任務,但添加一個SubAble會污染該集合。

申報

How can elements be added to a wildcard generic collection?

+0

「如果收藏已分配給SubAble,該怎麼辦?」那是什麼意思? –

+0

對不起,需要將它們標記爲代碼段,否則仿製零件不會顯示 – fmucar

0

Map<String,? extends Able> as; 

意味着 「任何地圖,具有字符串鍵,和值是的能子類型」。因此,舉例來說,你可以做到以下幾點:

Map<String,? extends Able> as = new HashMap<String, SubSubAble>(); 

現在讓我們來看看下面這個例子:

Map<String,? extends Able> as = new HashMap<String, SubSubAble>(); 
as.put("key", new A()); 

如果它是正確的,你會完成其HashMap中的內容{「鑰匙」,新的A()} - 這是類型錯誤!

0

Collection<?>超類型的各種收集。它不是a集合可以容納任何類型。至少這是我對整個概念的誤解。

我們可以用它,我們不關心泛型類型,就像這個例子:

public static void print(Collection<?> aCollection) { 
    for (Object o:aCollection) { 
    System.out.println(o); 
    } 
} 

如果我們選擇,而不是簽名:

public static void print(Collection<Object> aCollection) 

我們會我們只限於Collection<Object>類型的集合 - 換句話說,這種方法不會接受Collection<String>類型的值。

所以一個Collection<?>類型,可以採取任何類型的集合。它只需要未知類型。由於我們不知道該類型(它的未知;)),所以我們永遠不能添加一個值,因爲java中的類型不是未知類型的子類。

如果我們添加邊界(如<? extends Able>),類型爲仍然未知。

您正在尋找地圖聲明,其值全都實現了Able接口。正確的聲明很簡單:

Map<String, Able> map; 

假設我們有兩種類型AB子類化Able和兩個附加的地圖

Map<String, A> aMap; 
Map<String, B> bMap; 

和希望,方法返回的任何地圖,其值實現Able接口:然後我們使用通配符:

public Map<String, ? extends Able> createAorBMap(boolean flag) { 
return flag ? aMap: bMap; 
} 

(再次帶有約束,我們不能將新的鍵/值對添加到由此方法返回的映射)。

3

一個好辦法理解這個問題是讀什麼通配符的意思是:「一個地圖一種類型的擴展能的String類型的密鑰和值」

Map<String,? extends Able> as; 

不允許添加操作的原因是因爲他們「打開了大門」在集合中引入了不同的類型,這與打字系統有衝突。 例如

class UnAble implements Able; 
Map<String,UnAble> unableMap = new HashMap<String,UnAble>(); 
Map<String,? extends Able> ableMap = unableMap; 
ableMap.put("wontwork",new A()); // type mismatch: insert an A-type into an Unable map 

正確的使用通配符建設將是:

Result processAble(Map<String,? extends Able>) { ... read records & do something ... } 

Map<String,A> ableMap = new HashMap<String,A>; 
ableMap.put("willwork",new A()); 
processAble(as); 
processAble(unableMap); // from the definition above