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

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


当前回答

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

我们需要考虑两件事,

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类型的对象添加到列表中。

其他回答

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

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。

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

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从来都没有用。

根据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类型的对象。

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

Super是下界,extends是上界。

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

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