在我的应用程序中,我有一个主要基于两个层次结构的模型:容器和元素。容器通过列表引用元素。
例如,考虑一个Java类Child
,它是Parent
的子类型,ElementChild
是ElementParent
的子类型。这些元素用作列表的类型参数,我希望每个容器都可以添加,删除和获取列表中的元素。这些列表应尽可能具有限制性,即应包含放置在同一层次结构中的元素(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;
}
}
答案 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的子类,传入E
和L
类型参数的实际类型:
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 Parent
和Parent::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>
类似,但在您的位置,我会重新考虑您想要实现的目标,以及是否可以使用不同的方法。