层次结构和泛型

时间:2014-06-18 20:48:53

标签: java generics

在我的应用程序中,我有一个主要基于两个层次结构的模型:容器和元素。容器通过列表引用元素。
例如,考虑一个Java类Child,它是Parent的子类型,ElementChildElementParent的子类型。这些元素用作列表的类型参数,我希望每个容器都可以添加,删除和获取列表中的元素。这些列表应尽可能具有限制性,即应包含放置在同一层次结构中的元素(Child - ElementChild)。

我知道List<ElementParent>List<ElementChild>不属于子类型关系,是否有更好的方法,只需创建两个不同的列表List<ElementParent> Parent和{List<ElementChild> 1}}在Child)?

在下面的例子中,我使用了一个继承列表来显示我的意思,但显然它不能编译:

public class Parent {

    protected List<ElementParent> list;

    public Parent () {
        list = new ArrayList<ElementParent>();
    }

    public List<ElementParent> getList() {
        return list;
    }

    public void add(ElementParent element) {
        list.add(element);
    }

    public void remove (ElementParent element) {
        list.remove(element);
    }

    public void setList(List<ElementParent> list) {
        this.list = list;
    }
}

public class Child extends Parent {


        public Child() {
            list = new ArrayList<ElementChild>(); // does not compile
        }

        public List<ElementChild> getList() { //does not compile
            return list;
        }

        public void add(ElementChild element) {
            list.add(element);
        }

        public void remove (ElementChild element) {
            list.remove(element);
        }

        public void setList(List<ElementChild> list) { //does not compile
            this.list = list;
        }

}

2 个答案:

答案 0 :(得分:2)

这是一个棘手的问题,称为协变子类型。简而言之,这意味着当您扩展(a.k.a子类化)一个类并覆盖其某些方法时,您无法更改这些方法的参数类型。如果允许,编译器将无法对Java进行类型安全保证。

AFAIK解决问题的唯一方法是更多地依赖于泛型。具体来说,您需要定义一个类,该类在元素的类型和列表的类型上进行参数化:

public class Base<E, L extends List<E>> {

  protected L list;

  public Base(L l) { list = l; }     

  public L getList() { return list; }
  public void add(E element) { list.add(element); }
  public void remove(E element) { list.remove(element); }
  public void setList(L list) { this.list = list; }
}

然后您可以将Parent和Child定义为Base的子类,传入EL类型参数的实际类型:

public class Parent extends Base<ElementParent,List<ElementParent>> {    
  public Parent() {
    super(new ArrayList<ElementParent>());
  }    
}

public class Child extends Base<ElementChild, List<ElementChild>> {
  public Child() {
    super(new ArrayList<ElementChild>());
  }
}

答案 1 :(得分:1)

您的问题说明了为什么 List<ElementParent>List<ElementChild>不属于子类型关系

由于存在Child extends ParentParent::add(ElementParent element)方法,因此它会继承到Child

现在你可以写:

ElementParent ep = ...;
Child child = ...;
child.add(ep); //perfectly legal

但那会怎么样?将ElementParent添加到ElementChild列表中?这就是为什么 List<ElementParent>List<ElementChild>不属于子类型关系的原因

那么如何解决你的问题?

通配符可以在这里提供帮助,例如List<ElementParent>List<ElementChild>都是List<? extends ElementParent>,您可以从ElementParent阅读List<? extends ElementParent>,但不幸的是,您可以&# 39;写任何东西(null除外)。

与下限List<? super ElementChild>类似,但在您的位置,我会重新考虑您想要实现的目标,以及是否可以使用不同的方法。