Java集合协方差问题

Java集合协方差问题,java,design-patterns,generics,collections,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>

假设我们有一个包含以下类的程序:

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; }
}
公共接口抽象项{
}
公共项目实现抽象项目{
}
公共Bullititem实现了AbstractItem{
}
公共接口抽象工具箱{
//问题从这里开始。。。
公共列表getItems();
}
public ExpensiveToolbox实现了AbstractToolbox{
私有列表项=新的ArrayList();
public List getItems(){返回this.items;}
}
公共CheapTooblox实现了AbstractToolbox{
私有列表项=新的ArrayList();
public List getItems(){返回this.items;}
}
简单,对吗?假设我们现在想创建这样一个方法(在某个随机类中):

public void doImportantStuff(AbstractToolbox工具箱){
//重要的东西!
//这显然行不通
List items=toolbox.getItems();
//对所有项目进行一些填充
}
现在的问题是,在Java中,具有泛型的集合不是协变的(希望这是我正在寻找的术语),并且我不能将
ArrayList
分配给
列表。我在这里看到的唯一解决方案是复制代码并为每种类型创建一个版本,但这显然很糟糕(如果我们有更多的类使用不同的列表实现AbstractToolbox怎么办?)。哦,显然第二个解决方案是放弃泛型,并制作一个普通的列表,但是这是一个好的实践吗

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


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

您可能需要看看泛型的通配符类型。这里有一个快速链接:


快速回答:将类型更改为
List,这里有一些额外的想法。保持一切不变,但使用以下方法:

interface AbstractToolbox {
    public List<? extends AbstractItem> getItems();
}
或者,您可以键入AbstractToolbox:

public interface AbstractToolbox<T extends AbstractItem> {
    public List<T> getItems();
}
public ExpensiveToolbox implements AbstractToolbox<SharpItem> {
    public List<SharpItem> getItems() { //...
}
公共接口抽象工具箱{
公共列表getItems();
}
public ExpensiveToolbox实现了AbstractToolbox{
公共列表getItems(){/。。。
}
公共接口抽象项
{
}
公共类SharpItem实现AbstractItem
{
}
公共类BlintItem实现AbstractItem
{
}
公共接口抽象工具箱
{
公共列表getItems();
}
公共类ExpensiveToolbox实现了AbstractToolbox
{
私有列表项=新的ArrayList();
public List getItems(){返回this.items;}
}
公共类CheapToolbox实现了AbstractToolbox
{
私有列表项=新的ArrayList();
public List getItems(){返回this.items;}
}
公共void doImportantStuff(AbstractToolbox工具箱)
{

ListTo澄清一下,你不想让你的doImportantStuff有:List items=toolbox.getIems();你说的“简单,对吗?”你的第一部分没有编译,也不应该编译!我想这样做很容易。我的意思是,显然我在这方面有问题;)如果你花更多的精力发布示例,那就好了。你的示例中有大量语法和逻辑错误,这让你的答案变得复杂。例如,第一部分没有编译,你暗示它会编译,你暗示的第二部分显然不会编译,而它显然会编译!问题是,如果我知道如何编写一个编译和运行的示例(并按预期工作!)我不会有问题,因为它将编译并运行;)或者,或者,使用类型参数键入AbstractToolbox并使用它。但是,当您接受AbstractToolbox时,您需要处理参数t或通配符。如果您只需要处理AbstractItems,那么它具有您没有的好属性将类型参数添加到代码的其他部分。请参阅我回答的第一部分。一些误解是由于原始问题完全错误造成的。在文章中,第一部分没有编译,但考虑到AbstractToolbox的实现,后一部分会编译得很好,他说“显然不行。”又是一个粗心的示例代码。啊,对不起,我被冲突的信息弄糊涂了。您的示例代码让它更清楚:)+1用于键入AbstractToolbox,我认为这是显而易见的解决方案。
List<? extends AbstractItem> foo = new ArrayList<SharpItem>();
interface AbstractToolbox {
    public List<? extends AbstractItem> 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
public interface AbstractToolbox<T extends AbstractItem> {
    public List<T> getItems();
}
public ExpensiveToolbox implements AbstractToolbox<SharpItem> {
    public List<SharpItem> getItems() { //...
}
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) 
        ... ;

}