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

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


当前回答

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

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

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

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

其他回答

Super是下界,extends是上界。

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

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

的例子, 假设继承顺序为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是一个子类。我们不能将基类添加到子类,这就是发生错误的原因。另一种情况也是一样的。

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

我们需要考虑两件事,

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

何时使用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更常见是很有用的。