2009-11-15 138 views
0

當我使用泛型時,在某些類中出現錯誤。我指定了哪裏出錯。我無法擺脫它。我嘗試了演員陣容,以及我能想到的所有事情。Java泛型的問題

class UltraTable<O extends Object> { 
public void setContent(Collection<O> collection) { 
} 
} 

class ObjectA { 
} 

class AShell { 

protected UltraTable<? extends ObjectA> tableA; 

protected List<? extends ObjectA> getAObjects() { 
    return null; // the list is created here 
} 
} 

class BShell extends AShell { 
public BShell() { 
    tableA.setContent(getAObjects()); // THE ERROR IS HERE! 
} 

} 

我怎樣才能使它改變只在BShell類的工作,如果可能的話?

該錯誤消息我得到的是:

The method setContent(Collection<capture#1-of ? extends ObjectA>) 
in the type UltraTable<capture#1-of ? extends ObjectA> 
is not applicable for the arguments (List<capture#2-of ? extends ObjectA>) 

更新:

如果我改變我的代碼托馬斯·榮格說,我在tableA構造獲得其他類錯誤和方法getAObjects()

class ObjectX extends ObjectA {} 
class XShell extends AShell { 
    public XShell() { 
    tableA = new UltraTable<ObjectX>(); 
    tableA.setContent(getAObjects()); // THE SAME ERROR AS ABOVE 
    } 
    @Override 
    protected List<ObjectX> getAObjects() { 
    return null; 
    } 
} 

我經常有一個UltraTable,getAObjects()返回一個擴展BaseClass的DerivedClass列表。這應該適用於所有情況。

無論如何,我認爲錯誤信息是不合邏輯:「...... <capture#1-of ? extends ObjectA>是不適用的參數(List<capture#2-of ? extends ObjectA>)」
捕獲#1捕捉#2都擴展對象A!這裏有什麼問題?

回答

0

我知道答案,爲什麼你得到你的錯誤。 因爲我們都知道確保類型安全的原因,所以可以歸結爲如果使用通配符聲明集合,編譯器在運行時不會知道它所持有的實際對象的類是什麼,甚至如果在這種情況下它是ObjectA的一些子類。

由於這個原因,編譯器會讓你刪除對象,因爲它們已經被安全地存儲了,它知道它們是正確的類型,但是所有將對象添加到使用通配符類型聲明的集合的方法不起作用,comilier不會知道實際的類被存儲了,所以不能保證類型的安全。

作爲腳註參數化類型只存在於源代碼中,編譯器強制執行這些類型並確保只添加正確的對象。在運行時,任何List都是一個List,它並不重要,因爲編譯器確保只添加正確的對象。

class AShell <T extends ObjectA>{ 

protected UltraTable<T> tableA; 

protected List<T> getAObjects() { 
    return null; // the list is created here} 
} 

}

改變源代碼以上面的方法,它可以讓你創建一個UltraTableCollection TABLEA與對象A的任何子類,並返回被聲明爲類型的列表。 當你的shellB調用getObjects方法時,能夠存儲這個返回的List,因爲它繼承了原來定義方法的類中的List。 Ypou會得到一些編譯錯誤,但它仍然會編譯,編譯器會說你確定你將這個List存儲在正確的位置,這是因爲setContent方法,你現在得到的錯誤是由於getObjects () 方法。

我知道它很長,但它的泛型,需要一些解釋。

+0

ps。做很多單元測試。 – chrisg 2009-11-16 02:24:27

1

假設tableAUltraTable<String>getAObjects返回UltraTable<Integer>。你會打破類型系統。

也許你可能想要做的是genericise AShell

class AShell<T extends ObjectA> { 

    private UltraTable<T> tableA; 

    public List<T> getAObjects() { 
     ... 
+0

我試着像你說的,但在我的情況下,表和列表中的對象並不總是相同的類型。例如,我有一個'UltraTable ','getAObjects()'返回'DerivedClass'列表,它擴展了'BaseClass' – 2009-11-15 21:54:42

+0

因此,也許'UltraTable.setContents'應該使用'Collection '。 – 2009-11-16 00:14:08

4

的定義,如:

protected UltraTable<? extends ObjectA> tableA; 
protected List<? extends ObjectA> getAObjects(){} 

是沒有用的。你不能用UltraTable<ObjectA>做更多的事情。像這樣的定義只有在你正在使用它(作爲參數)時纔有用,而不是如果你正在生成它(作爲返回值)。例如一個定義:

public void setContent(Collection<? extends O> collection) {} 

可能是有用的。它現在可以使用O及其所有子類型的集合。

UltraTable<O extends Object>UltraTable<O>相同。所有O將是java.lang.Object的子類。

編譯。我希望它仍然有意義。

class UltraTable<O> { 
public void setContent(Collection<O> collection) {} 
} 
class ObjectA {} 

class AShell { 
protected UltraTable<ObjectA> tableA; 
protected List<ObjectA> getAObjects() { 
    return null; // the list is created here 
} 
} 

class BShell extends AShell { 
public BShell() { 
    tableA.setContent(getAObjects()); 
} 
} 

更新:

Java泛型是不變的。因此,唯一廣泛適用的解決方案是以XShell和AShell中的泛型類型參數相同的方式更改代碼。

class ObjectX extends ObjectA {} 
    class XShell extends AShell { 
    public XShell() { 
     tableA = new UltraTable<ObjectA>(); 
     tableA.setContent(getAObjects()); 
    } 
    @Override 
    protected List<ObjectA> getAObjects() { 
     return null; 
    } 
    } 

這是有道理的。因爲這將是無效的與XSHELL以取代的一枚:

AShell ashell; 
List<ObjectA> aobjects = ashell.getAObjects(); 
aobjects.add(new ObjectA()); 

XShell xshell; 
List<ObjectX> aobjects = xshell.getAObjects(); 
aobjects.add(new ObjectA()); //invalid: cannot cast to ObjectX 

但是,如果你不能與XSHELL以同樣的方式重新定義爲與一的一枚方法工作,它打破了Liskov substitution principle

+0

如果我照你說的,我會在其他地方的錯誤,如: 類_對象擴展對象A {} 類XSHELL擴展{的一枚公共 XSHELL(){ 表A =新UltraTable (); // tableA.setContent(getAObjects()); } @Override protected List getAObjects(){ return null; } } – 2009-11-15 21:34:59

+0

您能否將此添加到您的問題中?我無法在評論中解讀它。 – 2009-11-16 08:05:06

+0

我不能讓它工作,除非像chrisg說的那樣向AShell添加類型參數('class AShell ')。 – 2009-11-19 08:24:52