2010-09-21 63 views
5

假設我們有一個包含這樣的類的程序:Java集合協方差問題

public interface AbstractItem { 
} 
public SharpItem implements AbstractItem { 
} 
public BluntItem implements AbstractItem { 
} 

public interface AbstractToolbox { 
    //well the problem starts here... 
    public List<AbstractItem> getItems(); 
} 
public ExpensiveToolbox implements AbstractToolbox { 
    private List<SharpItem> items = new ArrayList()<SharpItems>; 
    public List<SharpItem> getItems() { return this.items; } 
} 
public CheapTooblox implements AbstractToolbox { 
    private List<BluntItem> items = new ArrayList()<BluntItem>; 
    public List<BluntItem> getItems() { return this.items; } 
} 

容易,對不對?那麼可以說,我們現在要做出這樣的方法(在一些隨機類):

public void doImportantStuff(AbstractToolbox toolbox) { 
//important stuff! 
//this obviously won't work 
    List<AbstractToolbox> items = toolbox.getItems(); 
//do some stuffwith all items 
} 

現在的問題是,在Java的泛型集合不是協變的(希望這是我要找的術語),我不能將ArrayList<ExpensiveToolbox>分配給List<AbstractToolbox>。我在這裏可以看到的唯一解決方案是複製代碼併爲每種類型做一個版本,但是這顯然會吸引人(如果我們有更多的類使用不同的列表實現AbstractToolbox?)。顯然,第二種解決方案是放棄泛型並制定一個正常的列表,但這是一個好習慣嗎?

是否有任何解決此類問題的設計模式/實踐?

@編輯:好吧,所以我可能不夠精確。我希望擴展AbstractToolbox的所有類都有一個擴展AbstractItem的特定類的列表,然後我需要一個將AbstractToolbox作爲參數並對列表中的項執行一些操作的方法(使用將在其中定義的類AbstractItem使每個可能的列表中的所有項目都會有它們)。

+0

爲了澄清,你會不會想你doImportantStuff有: 名單項目= toolbox.getIems(); – MikeTheReader 2010-09-21 18:21:04

+0

你是什麼意思,「容易,對嗎?」你的第一部分不編譯,不應該! – 2010-09-21 18:22:04

+1

那麼我認爲這將是容易的。我的意思很明顯*我*有問題;) – 2010-09-21 18:25:52

回答

16

您可能需要查看使用通配符類型的泛型。這裏有一個快速鏈接:What is PECS (Producer Extends Consumer Super)?

快速回答:類型更改爲List<? extends AbstractItem>

爲什麼你就不能分配呢?

想象這裏的代碼...

List<AbstractItem> foo = new ArrayList<SharpItem>(); 
foo.add(new BluntItem()); 

靜態類型說,這應該工作...但你不能做到這一點!它會違反ArrayList的類型。這就是爲什麼這是不允許的。如果您將其更改爲

List<? extends AbstractItem> foo = new ArrayList<SharpItem>(); 

然後您可以執行分配,但絕不會向列表中添加任何內容。不過,您仍然可以從列表中檢索元素作爲AbstractItems。

剛剛使用List(裸類型)一個很好的解決方案?

不,絕對不是:-P

+3

或者,也可以用類型參數鍵入AbstractToolbox,然後使用它。 – 2010-09-21 18:25:01

+0

但是當你接受一個AbstractToolbox時,你需要處理參數T或通配符*那*。如果你只需要處理AbstractItems,那麼它具有很好的屬性,你不必將類型參數添加到代碼的其他部分。 – 2010-09-21 18:26:34

+0

喜歡它,謝謝! – 2010-09-21 18:30:28

4

這裏有一些額外的想法。保持一切,但是使用這個:

interface AbstractToolbox { 
    public List<? extends AbstractItem> getItems(); 
} 

這基本上說抽象類的項目是未知的類型,但子類可以使它具體。這將需要您致電getItems()上ExpensiveToolbox或CheapToolbox類型的引用,以便能夠檢索允許您添加項目的列表等。

ExpensiveToolbox toolbox = new ExpensiveToolbox(); 
AbstractToolbox absTB = toolbox; 

List<? extends AbstractItem> items1 = absTB.getItems(); //fine 
List<SharpItem> items2 = absTB.getItems(); //compile error 
List<SharpItem> items3= toolbox.getItems(); //fine 

或者,你可以只輸入AbstractToolbox:

public interface AbstractToolbox<T extends AbstractItem> { 
    public List<T> getItems(); 
} 
public ExpensiveToolbox implements AbstractToolbox<SharpItem> { 
    public List<SharpItem> getItems() { //... 
} 
+0

+1鍵入AbstractToolbox,我認爲這是顯而易見的解決方案。 – 2010-09-21 19:44:33

0
public interface AbstractItem 
{ 
} 
public class SharpItem implements AbstractItem 
{ 
} 
public class BluntItem implements AbstractItem 
{ 
} 

public interface AbstractToolbox<T extends AbstractItem> 
{ 
    public List<T> getItems(); 
} 
public class ExpensiveToolbox implements AbstractToolbox<SharpItem> 
{ 
    private List<SharpItem> items = new ArrayList<SharpItem>(); 
    public List<SharpItem> getItems() { return this.items; } 
} 
public class CheapToolbox implements AbstractToolbox<BluntItem> 
{ 
    private List<BluntItem> items = new ArrayList<BluntItem>(); 
    public List<BluntItem> getItems() { return this.items; } 
} 


public void doImportantStuff(AbstractToolbox<?> toolbox) 
{ 
    List<? extends AbstractItem> items = toolbox.getItems(); 

    for(AbstractItem item : items) 
     ... ; 

}