差异<?超级T>和<?在Java中扩展T>

Ana*_*and 751 java generics collections

List<? super T>和之间有什么区别List<? extends T>

我曾经使用List<? extends T>,但它不允许我添加元素list.add(e),而List<? super T>它.

Ber*_*t F 1402

extends

通配符声明List<? extends Number> foo3表示任何这些都是合法的任务:

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number
Run Code Online (Sandbox Code Playgroud)
  1. 阅读 - 鉴于上述可能的分配,您可以保证从哪种类型的对象中读取List foo3:

    • 您可以读取a,Number因为可以分配的任何列表foo3包含a Number或子类Number.
    • 你不能读一个Integer因为foo3可能指向一个List<Double>.
    • 你不能读一个Double因为foo3可能指向一个List<Integer>.
  2. 写作 - 鉴于上述可能的分配,您可以添加哪种类型的对象List foo3对于上述所有可能的ArrayList分配都是合法的:

    • 你不能添加一个Integer因为foo3可能指向一个List<Double>.
    • 你不能添加一个Double因为foo3可能指向一个List<Integer>.
    • 你不能添加一个Number因为foo3可能指向一个List<Integer>.

您无法添加任何对象,List<? extends T>因为您无法保证List它实际指向的是什么类型,因此您无法保证该对象是允许的List.唯一的"保证"是你只能从它读取,你将得到一个T或子类 T.

super

现在考虑List <? super T>.

通配符声明List<? super Integer> foo3表示任何这些都是合法的任务:

List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
Run Code Online (Sandbox Code Playgroud)
  1. 阅读 - 鉴于上述可能的分配,当您阅读时,您保证会收到什么类型的对象List foo3:

    • 你不能保证,Integer因为foo3可能指向一个List<Number>List<Object>.
    • 你不能保证,Number因为foo3可能指向一个List<Object>.
    • 唯一的保证是,你会得到一个实例Object或子类的Object(但你不知道什么是子类).
  2. 写作 - 鉴于上述可能的分配,您可以添加哪种类型的对象List foo3对于上述所有可能的ArrayList分配都是合法的:

    • 您可以添加一个,Integer因为Integer上述任何列表都允许使用.
    • 您可以添加子类Integer的实例,因为Integer在上述任何列表中都允许子类的实例.
    • 你不能添加一个Double因为foo3可能指向一个ArrayList<Integer>.
    • 你不能添加一个Number因为foo3可能指向一个ArrayList<Integer>.
    • 你不能添加一个Object因为foo3可能指向一个ArrayList<Integer>.

佩奇

记住PECS:"制片人扩展,消费者超级".

  • "生产者扩展" - 如果您需要List生成T值(您希望T从列表中读取s),则需要声明它? extends T,例如List<? extends Integer>.但是您无法添加到此列表中.

  • "消费者超级" - 如果您需要List消费T值(您想要将Ts写入列表),您需要声明它? super T,例如List<? super Integer>.但是无法保证您可以从此列表中读取哪种类型的对象.

  • 如果您需要同时读取和写入列表,则需要在没有通配符的情况下完全声明它,例如List<Integer>.

请注意Java Generics FAQ中的此示例.请注意源列表src(生成列表)如何使用extends,以及目标列表dest(使用列表)使用super:

public class Collections { 
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++) 
        dest.set(i, src.get(i)); 
  } 
}
Run Code Online (Sandbox Code Playgroud)

另请参阅 如何添加到列表<?扩展Number>数据结构?

  • @BertF,很明确的解释,至少比doc.oracle的解释好3405691582倍 (49认同)
  • 在学习泛型之前,我曾经喜欢java. (11认同)
  • 我已经多次达到了这个答案.我可以不止一次投票.你知道你在[谷歌搜索结果]中排名第一(https://www.google.es/search?q=Difference%20 between%20%3C?%20super%20T%3E%20and%20%3C? %20extends%20T%3E%20英寸%20Java)? (7认同)
  • @Bert F'<?超级蔬菜>'确实*不*描述你可以添加到'foo'的内容 - 它描述了'foo'可以指向的列表类型."清单"?super VEGETABLE> foo'表示'foo'在任何给定时间指向List <VEGETABLE>或LIST <FOOD>,但你不知道哪个.因此,您只能提供双方都喜欢的"foo"内容.两个列表都可以给蔬菜或VEGETABLE的子类,但你不能给食物或肉类(因为列表<蔬菜>不能吃食物或肉类 - 已经是蔬菜). (7认同)
  • @Danon更喜欢那篇文章是这个答案的副本.这个答案来自2010年,该博客是从2016年开始的. (5认同)
  • @BertF:很好的解释.一个更正(如果是)你的意思是说'List <?超级整数> foo3`而不是`List <?超级数字> foo3`低于`super`? (4认同)
  • 漂亮的答案!不过,我有一个问题,你写了“你可以添加 Integer 子类的实例,因为上面任何列表中都允许 Integer 子类的实例” - 我认为你只能添加 Integer 的超类型,因为 &lt; ?超级整数&gt;? (3认同)
  • @Anand - 请参阅上面“PECS”下的第三个项目符号。您可能需要使用特定的通用参数(无通配符,例如“List&lt;T&gt;”)。不幸的是,你无法拥有一切。您可以使用通用通配符(例如“&lt;? super T&gt;”或“&lt;? extends T&gt;”)创建对列表的灵活引用,但您可以添加或读取的内容受到限制。或者您可以对具有特定类型(例如“List&lt;T&gt;”)的列表的不太灵活的引用,并更好地保证您可以从中读取/写入内容。 (2认同)
  • 为了完整起见, List&lt;? extends Number&gt; foo3,在写的部分,其实可以写null,如:foo3.add(null); (2认同)
  • 很好的解释!一个小小的注释:*"你可以添加一个Integer子类的实例,因为在上面的任何列表中都允许使用Integer子类的实例."* - 正确,除了`Integer`类被声明为final和因此无法延长. (2认同)
  • 这是一个想法:写一本书.我会买的.谢谢. (2认同)
  • 正如 @Danon 所提到的,Java 8 中的泛型有一个很好的解释 - https://nofluffjuststuff.com/magazine/2016/09/time_to_really_learn_generics_a_java_8_perspective (2认同)

Lui*_*ese 220

想象一下这个层次结构

在此输入图像描述

1.扩展

通过写作

    List<? extends C2> list;
Run Code Online (Sandbox Code Playgroud)

你说这list将能够引用一个类型的对象(例如),ArrayList其泛型类型是(包括)的7 个子类型之一:C2C2

  1. C2: new ArrayList<C2>();,(可以存储C2或子类型的对象)或
  2. D1: new ArrayList<D1>();,(可以存储D1或子类型的对象)或
  3. D2: new ArrayList<D2>();,(可以存储D2或子类型的对象)或......

等等.七种不同的情况:

    1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
    2) new ArrayList<D1>(): can store    D1    E1 E2  
    3) new ArrayList<D2>(): can store       D2       E3 E4
    4) new ArrayList<E1>(): can store          E1             
    5) new ArrayList<E2>(): can store             E2             
    6) new ArrayList<E3>(): can store                E3             
    7) new ArrayList<E4>(): can store                   E4             
Run Code Online (Sandbox Code Playgroud)

对于每种可能的情况,我们有一组"可存储"类型:此处以图形方式表示7(红色)集

在此输入图像描述

如您所见,并非每种情况都有一种安全类型:

  • 你不能,list.add(new C2(){});因为它可能list = new ArrayList<D1>();
  • 你不能,list.add(new D1(){});因为它可能list = new ArrayList<D2>();

等等.

超级

通过写作

    List<? super C2> list;
Run Code Online (Sandbox Code Playgroud)

你说这list将能够引用一个类型的对象(例如),ArrayList其泛型类型是(包括)的7个超类型之一:C2C2

  • A1: new ArrayList<A1>();,(可以存储A1或子类型的对象)或
  • A2: new ArrayList<A2>();,(可以存储A2或子类型的对象)或
  • A3: new ArrayList<A3>();,(可以存储A3或子类型的对象)或......

等等.七种不同的情况:

    1) new ArrayList<A1>(): can store A1          B1 B2       C1 C2    D1 D2 E1 E2 E3 E4
    2) new ArrayList<A2>(): can store    A2          B2       C1 C2    D1 D2 E1 E2 E3 E4
    3) new ArrayList<A3>(): can store       A3          B3       C2 C3 D1 D2 E1 E2 E3 E4
    4) new ArrayList<A4>(): can store          A4       B3 B4    C2 C3 D1 D2 E1 E2 E3 E4
    5) new ArrayList<B2>(): can store                B2       C1 C2    D1 D2 E1 E2 E3 E4
    6) new ArrayList<B3>(): can store                   B3       C2 C3 D1 D2 E1 E2 E3 E4
    7) new ArrayList<C2>(): can store                            C2    D1 D2 E1 E2 E3 E4
Run Code Online (Sandbox Code Playgroud)

对于每种可能的情况,我们有一组"可存储"类型:此处以图形方式表示7(红色)集

在此输入图像描述

正如你所看到的,在这里我们有七个安全类型是共同的每一种情况下:C2,D1,D2,E1,E2,E3,E4.

  • 你可以list.add(new C2(){});因为,无论我们引用哪种List,C2都是允许的
  • 你可以list.add(new D1(){});因为,无论我们引用哪种List,D1都是允许的

等等.您可能已经注意到这些类型对应于从类型开始的层次结构C2.

笔记

如果您想进行一些测试,这里是完整的层次结构

interface A1{}
interface A2{}
interface A3{}
interface A4{}

interface B1 extends A1{}
interface B2 extends A1,A2{}
interface B3 extends A3,A4{}
interface B4 extends A4{}

interface C1 extends B2{}
interface C2 extends B2,B3{}
interface C3 extends B3{}

interface D1 extends C1,C2{}
interface D2 extends C2{}

interface E1 extends D1{}
interface E2 extends D1{}
interface E3 extends D2{}
interface E4 extends D2{}
Run Code Online (Sandbox Code Playgroud)

  • 在PECS解释的世界中,它是最直接的 (5认同)
  • @Mike我不确定你的意思,这些节点在第二张图片中是蓝色的,不是任何一套/案例的一部分 (2认同)
  • 让我们[继续聊天中的讨论](http://chat.stackoverflow.com/rooms/112707/discussion-between-luigi-cortese-and-mike)。 (2认同)

Mic*_*ann 86

我喜欢@Bert F的答案,但这是我大脑看到它的方式.

我手里拿着一个X. 如果我想我的X写入List,那么List需要是一个X列表或一个我可以将X写入其中的事物列表,因为我将它们写入,即任何超类 X ...

List<? super   X>
Run Code Online (Sandbox Code Playgroud)

如果我得到一个List并且我想要读取该列表中的X,那么最好是X的列表或者在我读出它时可以向上转换为X的列表,即任何扩展 X的东西

List<? extends X>
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.


Sus*_*ant 19

根据Bert F的回答,我想解释一下我的理解.

可以说我们有3个班级

public class Fruit{}

public class Melon extends Fruit{}

public class WaterMelon extends Melon{}
Run Code Online (Sandbox Code Playgroud)

我们在这里

List<? extends Fruit> fruitExtendedList = …

//Says that I can be a list of any object as long as this object extends Fruit.
Run Code Online (Sandbox Code Playgroud)

好的,现在让我们尝试从fruitExtendedList获取一些值

Fruit fruit = fruitExtendedList.get(position)

//This is valid as it can only return Fruit or its subclass.
Run Code Online (Sandbox Code Playgroud)

再来试试吧

Melon melon = fruitExtendedList.get(position)

//This is not valid because fruitExtendedList can be a list of Fruit only, it may not be 
//list of Melon or WaterMelon and in java we cannot assign sub class object to 
//super class object reference without explicitly casting it.
Run Code Online (Sandbox Code Playgroud)

情况也是如此

WaterMelon waterMelon = fruitExtendedList.get(position)
Run Code Online (Sandbox Code Playgroud)

现在让我们尝试在fruitExtendedList中设置一些对象

添加水果对象

fruitExtendedList.add(new Fruit())

//This in not valid because as we know fruitExtendedList can be a list of any 
//object as long as this object extends Fruit. So what if it was the list of  
//WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon.
Run Code Online (Sandbox Code Playgroud)

添加Melon对象

fruitExtendedList.add(new Melon())

//This would be valid if fruitExtendedList was the list of Fruit but it may 
//not be, as it can also be the list of WaterMelon object. So, we see an invalid 
//condition already.
Run Code Online (Sandbox Code Playgroud)

最后让我们尝试添加WaterMelon对象

fruitExtendedList.add(new WaterMelon())

//Ok, we got it now we can finally write to fruitExtendedList as WaterMelon 
//can be added to the list of Fruit or Melon as any superclass reference can point 
//to its subclass object.
Run Code Online (Sandbox Code Playgroud)

但是等一下,如果有人决定制作一种新型的柠檬,可以说是为了争论SaltyLemon

public class SaltyLemon extends Lemon{}
Run Code Online (Sandbox Code Playgroud)

现在fruitExtendedList可以是Fruit,Melon,WaterMelon或SaltyLemon的列表.

所以,我们的声明

fruitExtendedList.add(new WaterMelon())
Run Code Online (Sandbox Code Playgroud)

也无效.

基本上我们可以说我们不能向fruitExtendedList写任何东西.

这总结了一下 List<? extends Fruit>

现在让我们看看

List<? super Melon> melonSuperList= …

//Says that I can be a list of anything as long as its object has super class of Melon.
Run Code Online (Sandbox Code Playgroud)

现在让我们尝试从melonSuperList中获取一些值

Fruit fruit = melonSuperList.get(position)

//This is not valid as melonSuperList can be a list of Object as in java all 
//the object extends from Object class. So, Object can be super class of Melon and 
//melonSuperList can be a list of Object type
Run Code Online (Sandbox Code Playgroud)

同样,Melon,WaterMelon或任何其他物体都无法读取.

但请注意,我们可以读取对象类型实例

Object myObject = melonSuperList.get(position)

//This is valid because Object cannot have any super class and above statement 
//can return only Fruit, Melon, WaterMelon or Object they all can be referenced by
//Object type reference.
Run Code Online (Sandbox Code Playgroud)

现在,让我们尝试从melonSuperList设置一些值.

添加对象类型对象

melonSuperList.add(new Object())

//This is not valid as melonSuperList can be a list of Fruit or Melon.
//Note that Melon itself can be considered as super class of Melon.
Run Code Online (Sandbox Code Playgroud)

添加Fruit类型对象

melonSuperList.add(new Fruit())

//This is also not valid as melonSuperList can be list of Melon
Run Code Online (Sandbox Code Playgroud)

添加Melon类型对象

melonSuperList.add(new Melon())

//This is valid because melonSuperList can be list of Object, Fruit or Melon and in 
//this entire list we can add Melon type object.
Run Code Online (Sandbox Code Playgroud)

添加WaterMelon类型对象

melonSuperList.add(new WaterMelon())

//This is also valid because of same reason as adding Melon
Run Code Online (Sandbox Code Playgroud)

总结一下,我们可以在melonSuperList中添加Melon或其子类,并且只读取Object类型对象.


Ist*_*tao 16

super是下限,extends是上限.

根据http://download.oracle.com/javase/tutorial/extra/generics/morefun.html:

解决方案是使用一种我们尚未见过的有界通配符形式:具有下限的通配符.语法?super T表示一个未知类型,它是T的超类型(或T本身;记住超类型关系是自反的).它是我们一直在使用的有界通配符的双重身份,我们在哪里使用?扩展T表示一个未知类型,它是T的子类型.


Ole*_*hov 12

我想想象一下这种差异.假设我们有:

class A { }
class B extends A { }
class C extends B { }
Run Code Online (Sandbox Code Playgroud)

List<? extends T> - 阅读和分配:

|-------------------------|-------------------|---------------------------------|
|         wildcard        |        get        |              assign             |
|-------------------------|-------------------|---------------------------------|
|    List<? extends C>    |    A    B    C    |                       List<C>   |
|-------------------------|-------------------|---------------------------------|
|    List<? extends B>    |    A    B         |             List<B>   List<C>   |
|-------------------------|-------------------|---------------------------------|
|    List<? extends A>    |    A              |   List<A>   List<B>   List<C>   |
|-------------------------|-------------------|---------------------------------|
Run Code Online (Sandbox Code Playgroud)

List<? super T> - 写作和分配:

|-------------------------|-------------------|-------------------------------------------|
|         wildcard        |        add        |                   assign                  |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super C>     |              C    |  List<Object>  List<A>  List<B>  List<C>  |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super B>     |         B    C    |  List<Object>  List<A>  List<B>           |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super A>     |    A    B    C    |  List<Object>  List<A>                    |
|-------------------------|-------------------|-------------------------------------------|
Run Code Online (Sandbox Code Playgroud)

在所有情况下:

  • 无论通配符如何,您始终可以 Object从列表中获取.
  • 无论通配符如何,您始终可以添加 null到可变列表中.


ely*_*yor 10

向列表中添加一个项目:

  • 列表< ? extends X >不允许添加任何东西,除了null到列表中。

  • 列表< ? super X >允许添加任何是 X(X 或其子类型)或 null 的内容。

从列表中获取项目:

  • 当您从List< 中获取项目时扩展 X >,您可以将其分配给 X 类型的变量或 X 的任何超类型,包括 Object。
  • 当您从List< 中获取项目时super X>,您只能将其分配给类型为 的变量Object

一些例子:

    List<? extends Number> list1 = new ArrayList<Integer>();
    list1.add(null);  //OK
    Number n = list1.get(0);  //OK
    Serializable s = list1.get(0);  //OK
    Object o = list1.get(0);  //OK

    list1.add(2.3);  //ERROR
    list1.add(5);  //ERROR
    list1.add(new Object());  //ERROR
    Integer i = list1.get(0);  //ERROR
Run Code Online (Sandbox Code Playgroud)
    List<? super Number> list2 = new ArrayList<Number>();
    list2.add(null);  //OK
    list2.add(2.3);  //OK
    list2.add(5);  //OK
    Object o = list2.get(0);  //OK

    list2.add(new Object());  //ERROR
    Number n = list2.get(0);  //ERROR
    Serializable s = list2.get(0);  //ERROR
    Integer i = list2.get(0);  //ERROR
Run Code Online (Sandbox Code Playgroud)


Sha*_*war 6

投票的答案涵盖了许多方面的细节。不过,我会尝试以不同的方式回答这个问题。

我们需要考虑两件事,

1. 给列表变量赋值

List<? extends X> listvar;

这里,任何X 列表或 X 子类列表都可以分配给 listvar。

List<? extends Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Integer>();


List<? super X> listvar;

这里,任何X 列表或 X 超类列表都可以分配给 listvar。

List<? super Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Object>();

2. 对列表变量进行读或写操作

`List<? extends X> listvar;`
Run Code Online (Sandbox Code Playgroud)

您可以使用此功能接受方法参数中的列表并对类型 X执行任何操作(注意:您只能从列表中读取类型 X 的对象)。

`List<? super Number> listvar;
Run Code Online (Sandbox Code Playgroud)

您可以使用此功能接受方法参数中的列表并对对象类型执行任何操作,因为您只能从列表中读取对象类型的对象。但是,是的,这里的附加事项是,您可以将类型 X 的对象添加到列表中。


Sai*_*der 5

使用扩展只能从集合中获取。你不能放进去。另外,虽然super允许 get 和 put,但 get 期间的返回类型是? 超级T。


jfo*_*x78 5

你可以通过上面所有的答案,明白为什么.add()被限制'<?>''<? extends>'以及部分原因'<? super>'

但是如果你想记住它,并且不想每次都去探索答案,这里是所有的结论:

List<? extends A>这意味着将接受任何ListA和子类A。但是您不能在此列表中添加任何内容。甚至不是类型的对象A

List<? super A>这意味着将接受任何列表A和超类A。您可以添加类型的对象A及其子类。