List的<? super E>和<? extends E>是什么意思?

19

以下是一个简单的类结构:

class A {
}

class B extends A {
}

class C extends B {
}
我正在创建一个ArrayList来保存之前创建的类的对象:
List<? extends A> list1 = new ArrayList<A>();
List<? extends B> list2 = new ArrayList<B>();
List<? extends C> list3 = new ArrayList<C>();

List<? super A> list4 = new ArrayList<A>();
List<? super B> list5 = new ArrayList<B>();
List<? super C> list6 = new ArrayList<C>();

我想向每个列表中添加一个之前创建的类A、B和C的对象,唯一可能的组合是:

  • 添加类A、B、C的对象到list4

  • 添加类B和C的对象到list5

  • 将类C的对象添加到list6。 尝试其他方法会导致编译器错误,例如:

List中的add(capture#1-of?extends A)方法不适用于参数(A)

为什么无法向list1/2/3添加A、B、C类的任何对象? 例如,如果它们被定义为A类的超类,为什么list4接受A、B、C类的对象?


1
我认为这个问题的答案在这里:https://dev59.com/VHNA5IYBdhLWcg3wjOlS - Ash
5个回答

15

"? extends A" 的意思是 "一些派生自 A (或 A 本身)的类型"。例如,List<ByteArrayOutputStream> 是与 List<? extends OutputStream> 兼容的 - 但你不应该能够将一个 FileOutputStream 添加到这样的列表中 - 它应该是一个 List<ByteArrayOutputStream>!你只知道从列表中获取的任何东西都将是某种类型的 OutputStream

"? super A" 的意思是 "一些 A 的超类(或 A 本身)的类型"。例如,List<OutputStream> 是与 List<? super ByteArrayOutputStream> 兼容的。你肯定可以将一个 ByteArrayOutputStream 添加到这样的列表中,但如果你从列表中获取一个项,你无法保证它的实际类型是什么。

请参阅 Angelika Langer 的泛型 FAQ 以获取更多信息。


谢谢,这是一个理解问题的好方法!但还有一件事情对我来说很奇怪 - 我发现你可以制作一个列表:List <? extends B> list2_2 = new ArrayList<A>();而不是:List <? extends B> list2 = new ArrayList<B>();然后仍然添加B类对象。 - qbaquak

4
类型定义 List<? extends A> 对于可变的 List 不可用 - 在 Java 泛型 Java Generics Pdf 中给出的解释是:

add() 方法采用集合元素类型 E 的参数。当实际类型参数为 ? 时,它表示某个未知类型。我们传递给 add 的任何参数都必须是此未知类型的子类型。由于我们不知道该类型是什么,因此我们不能传递任何内容。

然而,当 typedef 为 List<? super A> 时,类型参数 ? 隐式地被赋值。

2
List<? extends A> list1 

这是一个列表,其类型元素可以是A的任何未知子类。例如,它可以是D的子类。因此,您不能向其添加任何内容,否则可能会出错...

List<? super A> list4

这是一个列表,其类型元素可以是A,或A的超类(在这种情况下不存在,除了Object)。因此,您可以向其添加A对象,或任何A的子类,例如B或C。


1

它不是那样工作的。

当您创建一个函数,其参数是某种类型的未知子类型的集合,并且您想从该集合中获取对象时,应使用<? extends T>

int sum(Collection<? extends Integer> collection) {
    for (Integer : collection) {
        // do sth
    }
    // ...
}

你不能向此集合添加新项目,因为你不知道它所持有的具体类型是什么。你只知道这个类型扩展了Integer

当你想要向集合中添加类型为T的新项目并返回该集合时,你可以使用 <? super T> ,但你无法保证从中检索到的内容,需要强制转换 get() 的结果或检查其类型 。你可以安全地添加类型T和子类型的项目,并检索类型T的项。


1
使用 Number,Integer 是最终的 :) - Bax

0

List<? super B> 允许您使用任何 B 的超类型的列表,例如 list5 = new ArrayList<A>(); 或者 list5 = new ArrayList<Object>();

您可以安全地将 B(及其子类型)添加到使用 B 超类型的每个列表中,但是您不能添加任何 B 的超类型。想象一下:

public void myAdd(List<? super B> lst) {
  lst.add(new Object()) //this is a supertype of B (compile time error)
}
...
ArrayList<A> list = new ArrayList<A>();
myAdd(list); //tries to add Object to a list of type A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接