Java集合协方差问题

时间:2010-09-21 18:15:04

标签: java design-patterns generics collections

假设我们有一个包含这些类的程序:

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会怎么样?)。哦,显然第二个解决方案是放弃泛型并制作一个普通的List,但这是一个好习惯吗?

是否有任何设计模式/做法可以解决这些问题?

@Edit:好的,所以我可能不够准确。我希望扩展AbstractToolbox的所有类都有一个扩展AbstractItem的某些类的List然后我想要一个方法,它将AbstractToolbox作为参数并对其列表中的项执行某些操作(使用将在其中定义的类) AbstractItem所以每个可能列表的所有项目实际上都有它们。)

3 个答案:

答案 0 :(得分:21)

您可能需要查看使用通配符类型进行泛型。这是一个快速链接: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

答案 1 :(得分:5)

以下是一些额外的想法。保持一切相同,但使用此:

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

这基本上说抽象类的项是未知类型,但是子类可以使它具体化。这将要求您在ExpensiveToolbox或CheapToolbox类型的引用上调用getItems(),以便能够检索允许您添加项目的列表等。

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() { //...
}

答案 2 :(得分: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) 
        ... ;

}