我有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>
为什么会这样?
答案 0 :(得分:3)
问题是?
的值在运行时是未知的。您必须替换具体的类/接口才能够做您想做的事。
如果你这样做:
List<ElementA> list = new ArrayList<ElementA>();
你很好,因为ElementB
同时是 ElementA
。同样代表ElementC
。
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);
}
}
答案 1 :(得分:1)
List<ElementA>
接受ElementA
,ElementB
和Element C
的实例。
List<ElementB>
接受ElementB
和Element C
的实例。
List<ElementC>
接受ElementC
。
您的示例中没有使用通配符的原因。
List<? super ElementA>
表示某种类型的列表,ElementA
或超类。
List<? extends ElementB>
表示某种类型的List,它是ElementB
的子类。如果获取元素,它将是ElementB
或子类,但它不知道该类是什么,因此无法确定您添加的元素是否正确类型,因为它是未知的(尽管它确实知道它是ElementB
的子类)。
通配符有用,但你的例子不是其中之一。
答案 2 :(得分:0)
您可以像这样创建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
编译器执行此操作,以便之前的情况永远不会发生。