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中,泛型的集合不是协变的(希望这是我正在寻找的术语),并且我无法将一个分配给.我在这里看到的唯一解决方案是复制代码并为每种类型做一个版本,但这显然会很糟糕(如果我们有更多的类实现具有不同列表的 AbstractToolbox 呢?)。哦,显然第二种解决方案是删除泛型并制作一个正常的列表,但这是一种好的做法吗?ArrayList<ExpensiveToolbox>List<AbstractToolbox>

是否有任何设计模式/实践来解决此类问题?

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


答案 1

您可能需要考虑对泛型使用通配符类型。这里有一个快速链接:什么是PECS(生产者扩展消费者超级)?

快速回答:将类型更改为List<? extends AbstractItem>

为什么不能只分配这个?

想象一下这里的代码...

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

静态类型说这应该有效...但你不能那样做!这将违反 ArrayList 的类型。这就是为什么这是不允许的。如果将其更改为

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

然后,您可以执行分配,但切勿向列表中添加任何内容。但是,您仍然可以从列表中检索元素作为抽象项。

只是使用列表(裸类型)是一个好的解决方案吗?

不,绝对不是:-p


答案 2

这里有一些额外的想法。保持所有内容不变,但请使用以下命令:

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