和 for List

具有以下简单的类结构:

class A { } class B extends A { } class C extends B { } 

我正在创建一个ArrayList来保存先前创建的类的对象:

 List list1 = new ArrayList(); List list2 = new ArrayList(); List list3 = new ArrayList(); List list4 = new ArrayList(); List list5 = new ArrayList(); List list6 = new ArrayList(); 

对于每个列表,我正在尝试添加每个先前创建的类的1个对象:A,B,C。 唯一可能的组合是:

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

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

  • 将类C的对象添加到列表list6。 其余的尝试给出了编译器错误,例如:

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

为什么我不能将类A,B,C的任何对象添加到list1 / 2/3? 为什么例如list4接受类A,B,C的对象,如果它们应该是A类的超类,因为list4是定义的?

“?extends A”表示“某种类型派生自A(或A本身)”。 例如, ListList兼容 List – 但你不应该将FileOutputStream添加到这样的列表 – 它应该是一个List ! 你所知道的是,你列表中获取的任何东西都是某种类型的OutputStream

“?super A”表示“某种类型,它是A(或A本身)的超类”。 例如, ListList兼容 List 。 你绝对可以将ByteArrayOutputStream添加到这样的列表中 – 但是如果你从列表中获取一个项目,你就无法真正保证它。

有关更多信息,请参阅Angelika Langer的Generics FAQ 。

类型定义List List不适用于可变列表 – JavagenericsJava Generics Pdf中给出的解释是

add()方法接受类型为E的参数,即集合的元素类型。 当实际类型参数是?时,它代表某种未知类型。 我们传递给add的任何参数都必须是这种未知类型的子类型。 既然我们不知道那是什么类型,我们就无法传递任何东西。

但是,当typedef是List List那么类型参数? 是隐式输入的。

 List list1 

它是一个列表,其type元素可以是A的任何未知子类。例如,它可以是D子类。 因此,你不能添加任何东西,它可能是错的……

 List list4 

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

它不起作用。

你应该用 在创建函数时哪个参数是某种类型的未知子类型的集合,并且您想要从该集合中获取对象:

 int sum(Collection collection) { for (Integer : collection) { // do sth } // ... } 

您无法向此集合添加新项目,因为您不知道此集合包含哪种具体类型。 你所知道的是那种类型扩展了Integer

你用 当你想要将T类型的新项目添加到集合并返回该集合时,但是你不能保证你可以从它中检索什么,你必须转换get()结果或检查它的类型。 您可以安全地添加T类型和子类型的项目,并检索T类型的项目。

List List允许你使用任何超类型B的列表,即list5 = new ArrayList (); 或list5 = new ArrayList < Object >();

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

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