List<之间的区别是什么?super T>和List<?延伸T> ?

我曾经使用List<?扩展T>,但它不允许我添加元素到它的List .add(e),而List<?super T>有。


Super是下界,extends是上界。

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

解决方法是使用一种形式的 有界通配符我们还没见过 具有下界的通配符。的 语法呢?超级T表示未知 类型是T(或T 本身;记住超类型 关系是自反的)。它是双重的 有界通配符 使用,我们在哪里使用?将T延伸到 表示未知类型 T的子型。


扩展

List<?数字> 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

Reading - Given the above possible assignments, what type of object are you guaranteed to read from List foo3: You can read a Number because any of the lists that could be assigned to foo3 contain a Number or a subclass of Number. You can't read an Integer because foo3 could be pointing at a List<Double>. You can't read a Double because foo3 could be pointing at a List<Integer>. Writing - Given the above possible assignments, what type of object could you add to List foo3 that would be legal for all the above possible ArrayList assignments: You can't add an Integer because foo3 could be pointing at a List<Double>. You can't add a Double because foo3 could be pointing at a List<Integer>. You can't add a Number because foo3 could be pointing at a List<Integer>.

你不能向List<?extends t>因为你不能保证它真正指向的是什么样的List,所以你不能保证对象被允许在那个List中。唯一的“保证”是你只能读取它,你会得到一个T或T的子类。

超级

现在考虑List <?超级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

Reading - Given the above possible assignments, what type of object are you guaranteed to receive when you read from List foo3: You aren't guaranteed an Integer because foo3 could be pointing at a List<Number> or List<Object>. You aren't guaranteed a Number because foo3 could be pointing at a List<Object>. The only guarantee is that you will get an instance of an Object or subclass of Object (but you don't know what subclass). Writing - Given the above possible assignments, what type of object could you add to List foo3 that would be legal for all the above possible ArrayList assignments: You can add an Integer because an Integer is allowed in any of above lists. You can add an instance of a subclass of Integer because an instance of a subclass of Integer is allowed in any of the above lists. You can't add a Double because foo3 could be pointing at an ArrayList<Integer>. You can't add a Number because foo3 could be pointing at an ArrayList<Integer>. You can't add an Object because foo3 could be pointing at an ArrayList<Integer>.

PECS

记住PECS:“生产者延伸,消费者至上”。

"Producer Extends" - If you need a List to produce T values (you want to read Ts from the list), you need to declare it with ? extends T, e.g. List<? extends Integer>. But you cannot add to this list. "Consumer Super" - If you need a List to consume T values (you want to write Ts into the list), you need to declare it with ? super T, e.g. List<? super Integer>. But there are no guarantees what type of object you may read from this list. If you need to both read from and write to a list, you need to declare it exactly with no wildcards, e.g. List<Integer>.

例子

请注意这个来自Java泛型常见问题解答的例子。注意源列表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)); 
  } 
}

也看到 如何添加到List<?extends数字>数据结构?


使用只能从集合中获得的扩展。你不能投入进去。此外,虽然超级允许获取和放置,在获取期间的返回类型是?超级T。


通用通配符针对两个主要需求:

从泛型集合读取 插入到泛型集合中 有三种使用通配符定义集合(变量)的方法。这些都是:

List<?>           listUknown = new ArrayList<A>();
List<? extends A> listUknown = new ArrayList<A>();
List<? super   A> listUknown = new ArrayList<A>();

列表< ?>表示输入未知类型的列表。这可以是一个列表< a >,一个列表<B>,一个列表<字符串>等。

列表< ?extends A>表示A类的实例或A的子类(例如B和C)的对象列表。 列表< ?super A>表示列表被输入到A类或A的超类。

阅读更多信息:http://tutorials.jenkov.com/java-generics/wildcards.html


想象一下有这样的层次结构

1. 扩展

通过编写

    List<? extends C2> list;

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

C2: new ArrayList<C2>();,(可以存储C2或子类型的对象)或 D1: new ArrayList<D1>();,(可以存储D1或子类型的对象)或 D2: new ArrayList<D2>();,(可以存储D2或子类型的对象)or…

等等。七种不同的情况:

    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             

对于每种可能的情况,我们都有一组“可存储”类型:这里图形化地表示了7个(红色)集

正如你所看到的,并没有一种安全的类型适用于所有情况:

你不能列举。添加(新的C2 () {});因为可以是list = new ArrayList<D1>(); 你不能列举。添加(新D1 () {});因为可以是list = new ArrayList<D2>();

等等。

2. 超级

通过编写

    List<? super C2> list;

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

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

等等。七种不同的情况:

    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

对于每种可能的情况,我们都有一组“可存储”类型:这里图形化地表示了7个(红色)集

正如你所看到的,这里有7种安全类型,在每种情况下都是常见的:C2, D1, D2, E1, E2, E3, E4。

你可以列出。添加(新的C2 () {});因为,不管我们引用的是哪种List, C2都是允许的 你可以列出。添加(新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{}

我喜欢来自@Bert F的答案,但这是我大脑看到的方式。

我手里有个X。如果我想把我的X写进一个列表,这个列表需要是一个X的列表,或者是一个我写X时可以向上转换的列表,即X的任何超类…

List<? super   X>

如果我得到一个列表,我想从这个列表中读取一个X,那最好是一个X的列表,或者是一个可以在我读取它们时向上转换为X的列表,即任何扩展X的东西

List<? extends X>

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

假设我们有3个类

public class Fruit{}

public class Melon extends Fruit{}

public class WaterMelon extends Melon{}

这里我们有

List<? extends Fruit> fruitExtendedList = …

//Says that I can be a list of any object as long as this object extends Fruit.

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

Fruit fruit = fruitExtendedList.get(position)

//This is valid as it can only return Fruit or its subclass.

让我们再试一次

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.

的情况也是一样

WaterMelon waterMelon = fruitExtendedList.get(position)

现在让我们尝试在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.

添加甜瓜对象

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.

最后让我们尝试添加西瓜对象

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.

但是等等,如果有人决定制造一种新的柠檬,让我们说,为了争论,SaltyLemon作为

public class SaltyLemon extends Lemon{}

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

因此,我们的声明

fruitExtendedList.add(new WaterMelon())

也是无效的。

基本上,我们可以说不能向fruitExtendedList中写入任何内容。

这对List<?扩展了水果>

现在让我们看看

List<? super Melon> melonSuperList= …

//Says that I can be a list of anything as long as its object has super class of Melon.

现在让我们尝试从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

同样地,Melon、Melon或其他任何对象都不能读取。

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

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.

现在,让我们尝试从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.

添加水果类型对象

melonSuperList.add(new Fruit())

//This is also not valid as melonSuperList can be list of Melon

添加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.

添加西瓜类型对象

melonSuperList.add(new WaterMelon())

//This is also valid because of same reason as adding Melon

综上所述,我们可以在melonSuperList中添加Melon或其子类,并且只读取Object类型的对象。


这里最令人困惑的是,无论我们指定了什么类型限制,赋值只能以一种方式工作:

baseClassInstance = derivedClassInstance;

您可能认为Integer扩展了Number,并且Integer可以作为<?扩展数字>,但是编译器会告诉你<?extends Number>不能转换为Integer(也就是说,在人类的说法中,任何扩展Number的东西都可以转换为Integer是错误的):

class Holder<T> {
    T v;
    T get() { return v; }
    void set(T n) { v=n; }
}
class A {
    public static void main(String[]args) {
        Holder<? extends Number> he = new Holder();
        Holder<? super Number> hs = new Holder();

        Integer i;
        Number n;
        Object o;

        // Producer Super: always gives an error except
        //       when consumer expects just Object
        i = hs.get(); // <? super Number> cannot be converted to Integer
        n = hs.get(); // <? super Number> cannot be converted to Number
                      // <? super Number> cannot be converted to ... (but
                      //       there is no class between Number and Object)
        o = hs.get();

        // Consumer Super
        hs.set(i);
        hs.set(n);
        hs.set(o); // Object cannot be converted to <? super Number>

        // Producer Extends
        i = he.get(); // <? extends Number> cannot be converted to Integer
        n = he.get();
        o = he.get();

        // Consumer Extends: always gives an error
        he.set(i); // Integer cannot be converted to <? extends Number>
        he.set(n); // Number cannot be converted to <? extends Number>
        he.set(o); // Object cannot be converted to <? extends Number>
    }
}

hs.set(我);是可以的,因为Integer可以转换为Number的任何超类(而不是因为Integer是Number的超类,这不是真的)。

EDIT添加了一条关于消费者扩展和生产者超级的注释——它们没有意义,因为它们相应地指定了什么,而只是对象。建议您记住PECS,因为CEPS从来都没有用。


何时使用extends和super

通配符在方法参数中最有用。它们允许方法接口中必要的灵活性。

人们经常混淆什么时候使用扩展,什么时候使用超边界。经验法则是“得到-放”原则。如果您从参数化容器中获取某些内容,请使用extends。

int totalFuel(List<? extends Vehicle> list) {
int total = 0;
for(Vehicle v : list) {
    total += v.getFuel();
}
return total;}

totalFuel方法从列表中获取车辆,询问它们有多少燃料,并计算总数。 如果将对象放入参数化容器中,请使用super。

int totalValue(Valuer<? super Vehicle> valuer) {
int total = 0;
for(Vehicle v : vehicles) {
    total += valuer.evaluate(v);
}
return total;}

totalValue方法将车辆放入Valuer中。 知道扩展界比super更常见是很有用的。


你可以通过上面所有的答案来理解为什么.add()被限制为'<?>”、“< ?扩展>',部分扩展到'<?超级>”。

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

列表< ?extends A>表示它将接受A的任何List和A的子类。 但是你不能在这个列表中添加任何东西。甚至不是A类型的对象。

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


向列表中添加一个项目:

列表< ?extends X >不允许向列表中添加任何东西,除了null。 列表< ?super X >允许添加任何-a 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

    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

向上投票的答案涵盖了很多方面的细节。然而,我会尝试用不同的方式来回答这个问题。

我们需要考虑两件事,

1. 对列表变量赋值

列表< ?扩展X> listvar;

在这里,X的任何列表或X的子类列表都可以赋值给listvar。

列表<? 扩展 数字> 列表变量; listvar = new ArrayList<Number>(); listvar = new ArrayList<Integer>();


列表< ?super X> listvar;

在这里,X的任何列表或X的超类列表都可以赋值给listvar。

列表<? 超级数字>列表变量; listvar = new ArrayList<Number>(); listvar = new ArrayList<Object>();

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

`List<? extends X> listvar;`

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

`List<? super Number> listvar;

您可以使用此特性在方法参数中接受列表,并对Object类型执行任何操作,因为您只能从列表中读取Object类型的对象。是的,这里还有一点,你可以把X类型的对象添加到列表中。


的例子, 假设继承顺序为O > S > T > U > V

使用extends关键字,

正确的:

List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();

不正确的:

List<? extends T> Object = new List<S>();
List<? extends T> Object = new List<O>();

超级关键字:

正确的:

List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();

不正确的:

List<? super T> Object = new List<U>();
List<? super T> Object = new List<V>();

添加对象: List对象= new List();

Object.add(new T()); //error

但是为什么会出错呢? 让我们看看初始化列表对象的可能性

List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();

如果我们使用Object。添加(新T ());那么只有当

List<? extends T> Object = new List<T>(); 

但还有另外两种可能

List对象= new List(); List对象= new List(); 如果我们尝试将(new T())添加到上述两个可能性中,它将会给出一个错误,因为T是U和V的高级类。我们尝试添加一个T对象[它是(new T())]到类型为U和V的列表。高级类对象(基类)不能传递给低级对象(子类)。

由于额外的两种可能性,即使你使用了正确的可能性,Java也会给你错误,因为Java不知道你所指的对象是什么,所以你不能向List中添加对象Object = new List();因为有不成立的可能性。

添加对象: List对象= new List();

Object.add(new T()); // compiles fine without error
Object.add(new U()); // compiles fine without error
Object.add(new V()); // compiles fine without error

Object.add(new S()); //  error
Object.add(new O()); //  error

但是为什么上面两个会出现错误呢? 我们可以使用Object。添加(新T ());只有在以下的可能性中,

List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();

如果我们尝试使用Object。add(new T()) in List对象= new List(); 而且 List对象= new List(); 那么它就会给出错误 这是因为 我们不能将T对象[which is new T()]添加到List object = new List();因为它是U类型的对象。我们不能给U对象添加一个T对象[它是新的T()],因为T是一个基类,U是一个子类。我们不能将基类添加到子类,这就是发生错误的原因。另一种情况也是一样的。


我想把区别形象化。假设我们有:

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

列表< ?扩展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>   |
|-------------------------|-------------------|---------------------------------|

列表< ?超级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>                    |
|-------------------------|-------------------|-------------------------------------------|

在所有的情况下:

不管通配符是什么,你总是可以从列表中获得Object。 不管通配符是什么,你总是可以在可变列表中添加null。