[英]Generic types in Java - advanced polymorphism
我有3个简单的类,如下所示:
public class ElementA {}
public class ElementB extends ElementA {}
public class ElementC extends ElementB {}
然后,如果我想创建例如仅包含ElementA类的子类的通用List,则可以将其声明为:
List<? super ElementA> list = new ArrayList<>();
然后按如下方式使用它:
list.add(new ElementA());
list.add(new ElementB());
list.add(new ElementC());
很好,可以编译而不会出错。 但是,如果我想存储除ElementC或ElementB或ElementA以外的任何内容,我会感到困惑。 我声明这样的列表如下:
List<? extends ElementC> list = new ArrayList<>();
而且我根本无法使用它,因为它只能存储空值。 当我将List声明为(注意,我正在使用位于“家庭中间”的类)时,会发生相同的事情:
List<? extends ElementB>
为什么这样?
问题在于?
在运行时未知。 您必须替换具体的类/接口才能执行所需的操作。
如果您这样做:
List<ElementA> list = new ArrayList<ElementA>();
您很好,因为ElementB
是 ElementA
。 同样代表ElementC
。
List<? extends ElementA>
如果您在类中声明它,并且在子类中可以将一些具体的东西替换为type参数,则List<? extends ElementA>
有意义。 笨拙的例子:
public class SomeClass<T> {
private List<? extends T> list;
public void setList(List<? extends T> list) {
this.list = list;
}
}
public class SomeConcreteClass extends SomeClass<Integer> {
public void doSomething() {
List<Integer> list = new ArrayList<Integer>();
setList(list);
}
}
List<ElementA>
接受ElementA
, ElementB
和Element C
实例。
List<ElementB>
接受ElementB
和Element C
实例。
List<ElementC>
接受ElementC
实例。
您的示例中没有理由使用通配符。
List<? super ElementA>
List<? super ElementA>
表示某种类型的List,它是ElementA
或超类。
List<? extends ElementB>
List<? extends ElementB>
表示某种类型的List,它是ElementB
的子类。 如果得到一个元素,它将是ElementB
或子类,但是它不知道该类是什么,因此不能确定添加的元素的类型正确,因为它是未知的(尽管它确实知道它是ElementB
的子类)。
通配符有一些用途,但您的示例不是其中之一。
您创建一个这样的List
List<? extends ElementC> list = new ArrayList<>();
但可以说,因为这样获得List
仍然有效
List<? extends ElementC> list = getElementCSubclassList(); // declared as returning a `List<ElementCSubclass>`
现在,编译器无法知道您的list
对象包含ElementCSubclass
对象,因此只能确保它包含某种类型的ElementC
。 因此,它不能让您使用任何期望实际通用类型的方法。
想像
public class ElementCSubclass1 extends ElementC {}
public class ElementCSubclass2 extends ElementC {}
...
List<? extends ElementC> list = getElementCSubclass1List(); // declared as returning a `List<ElementCSubclass1>`
list.add(new ElementCSubclass2()); // this would immediately have to fail
编译器这样做是为了使以前的情况永远不会发生。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.