代码之家  ›  专栏  ›  技术社区  ›  Mateusz Dymczyk

Java集合协方差问题

  •  8
  • Mateusz Dymczyk  · 技术社区  · 14 年前

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

    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中定义的类,以便每个可能列表中的所有项实际上都具有他们)。

    3 回复  |  直到 14 年前
        1
  •  23
  •   Community CDub    8 年前

    您可能需要研究一下对泛型使用通配符类型。这里有一个快速链接: What is PECS (Producer Extends Consumer Super)?

    List<? extends AbstractItem>

    为什么你不能分配这个?

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

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

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

    然后你可以做作业,但不要在列表中添加任何内容。但是,您仍然可以从列表中检索作为抽象项的元素。

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

    不,绝对不是

        2
  •  5
  •   Mark Peters    14 年前

    这里有一些额外的想法。一切保持不变,但使用以下方法:

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

    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() { //...
    }
    
        3
  •  0
  •   irreputable    14 年前
    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) 
            ... ;
    
    }