【问题标题】:Hierarchies and generics层次结构和泛型
【发布时间】:2014-06-18 20:48:53
【问题描述】:

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

我知道List<ElementParent>List<ElementChild> 不是子类型关系,有没有比创建两个不同的列表更好的方法(Parent 中的List<ElementParent>Child 中的List<ElementChild>)?

在以下示例中,我使用继承列表来说明我的意思,但显然它无法编译:

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;
        }

}

【问题讨论】:

  • 可以把相关代码放在这里吗?你的哪些类是通用的?对我来说听起来没有。
  • List&lt;? extends ElementParent&gt;.
  • @christopher:那将是一个仅限生产者的列表。
  • 这里的泛型就是列表,每个容器都应该有一个列表的引用,元素就是类型参数。让我想一个清晰的代码,我会发布它。
  • 类似的解决方案是否也适用于您:stackoverflow.com/questions/24292827/…(引入另一个通用参数)。

标签: java generics


【解决方案1】:

这是一个棘手的问题,称为协变子类型。简而言之,这意味着当您扩展(也称为子类化)一个类并覆盖其某些方法时,您无法更改这些方法的参数类型。如果允许,编译器将无法保证 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>());
  }
}

【讨论】:

    【解决方案2】:

    您的问题说明了为什么 List&lt;ElementParent&gt;List&lt;ElementChild&gt; 不在子类型关系中

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

    现在你可以写了:

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

    但是那会做什么呢?将ElementParent 添加到ElementChilds 列表?这就是 List&lt;ElementParent&gt;List&lt;ElementChild&gt; 不属于子类型关系的原因

    那么如何解决你的问题呢?

    通配符可以在这里提供帮助,例如 List&lt;ElementParent&gt;List&lt;ElementChild&gt; 都是 List&lt;? extends ElementParent&gt; 并且您可以从 List&lt;? extends ElementParent&gt; 读取 ElementParent,但不幸的是,您无法向其写入任何内容(null 除外)。

    与下限List&lt;? super ElementChild&gt; 类似,但在您的位置上,我会重新考虑您究竟想要达到什么目标,以及是否可以使用不同的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-14
      • 2013-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多