List<? super T>
和List<? extends T>
之间的区别是什么?
我过去常常使用List<? extends T>
,但它不允许我向list.add(e)
添加元素,而List<? super T>
则会这样做。
答案 0 :(得分:1295)
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
阅读 - 鉴于上述可能的分配,您可以保证从List foo3
读取哪种类型的对象:
Number
,因为可以分配给foo3
的任何列表都包含Number
或Number
的子类Integer
,因为foo3
可能指向List<Double>
。Double
,因为foo3
可能指向List<Integer>
。 写作 - 鉴于上述可能的分配,您可以向List foo3
添加哪种类型的对象,这对于所有以上可能是合法的ArrayList
作业:
Integer
,因为foo3
可能指向List<Double>
。Double
,因为foo3
可能指向List<Integer>
。Number
,因为foo3
可能指向List<Integer>
。 您无法向List<? extends T>
添加任何对象,因为您无法保证它实际指向的是哪种List
,因此您无法保证该对象是允许的在那List
。唯一的“保证”是您只能从中读取,并且您将获得T
的{{1}}或子类。
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
阅读时,您保证会收到什么类型的对象:
List foo3
可能指向Integer
或foo3
,因此无法保证List<Number>
。List<Object>
可能指向Number
,因此无法保证foo3
。List<Object>
的实例或Object
的子类(但您不知道是什么子类)。 写作 - 鉴于上述可能的分配,您可以向Object
添加哪种类型的对象,这对于所有以上可能是合法的List foo3
作业:
ArrayList
,因为上述任何一个列表都允许Integer
。Integer
子类的实例,因为在上述任何列表中都允许Integer
的子类实例。Integer
,因为Double
可能指向foo3
。ArrayList<Integer>
,因为Number
可能指向foo3
。ArrayList<Integer>
,因为Object
可能指向foo3
。记住 PECS :“制作人扩展,消费者超级”。
“制作者扩展” - 如果您需要ArrayList<Integer>
来生成List
值(您希望从列表中读取T
) ,你需要用T
声明它,例如? extends T
。但您无法添加到此列表中。
“消费者超级” - 如果您需要List<? extends Integer>
消费List
值(您希望将T
写入列表) ,你需要用T
声明它,例如? super T
。但是无法保证您可以从此列表中读取什么类型的对象。
如果您需要同时读取和写入列表,则需要在没有通配符的情况下完全声明它,例如: List<? super Integer>
。
注意this example from the Java Generics FAQ。请注意源列表List<Integer>
(生成列表)如何使用src
,目标列表extends
(使用列表)使用dest
:
super
答案 1 :(得分:203)
想象一下这个层次结构
写作
List<? extends C2> list;
您说list
将能够引用类型的对象(例如)ArrayList
,其泛型类型是{{1>的7个子类型之一}(包括C2
):
C2
,(可存储C2或子类型的对象)或new ArrayList<C2>();
,(可存储D1或子类型的对象)或new ArrayList<D1>();
,(可存储D2或子类型的对象)或...... 等等。七种不同的案例:
new ArrayList<D2>();
我们为每种可能的案例都有一组“可存储”类型:7(红色)集合在这里用图形表示
如您所见,每种情况都没有安全类型:
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
,因为它可能是list.add(new C2(){});
list = new ArrayList<D1>();
,因为它可能是list.add(new D1(){});
等等。
写作
list = new ArrayList<D2>();
你说 List<? super C2> list;
将能够引用类型的对象(例如)list
,其泛型类型是{{1>的7个超类型之一}(包括ArrayList
):
C2
,(可存储A1或子类型的对象)或C2
,(可以存储A2或子类型的对象)或new ArrayList<A1>();
,(可以存储A3或子类型的对象)或...... 等等。七种不同的案例:
new ArrayList<A2>();
我们为每种可能的案例都有一组“可存储”类型:7(红色)集合在这里用图形表示
正如您所看到的,此处我们有七种安全类型,每种情况都相同:new ArrayList<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
,C2
,{{1} },D1
,D2
,E1
。
E2
因为,无论我们引用哪种列表,都允许E3
E4
因为,无论我们引用哪种列表,都允许list.add(new C2(){});
等等。您可能已经注意到这些类型对应于从类型C2
开始的层次结构。
如果你想进行一些测试,这里是完整的层次结构
list.add(new D1(){});
答案 2 :(得分:75)
我喜欢@Bert F的答案,但这是我大脑看到它的方式。
我手里拿着一个X.如果我想将我的X写入List,那么List必须是X的列表或者我可以将其写入的内容列表,因为我将其写入,即任何 X的超类 ...List<? extends X>
如果我得到一个List并且我希望读取该列表中的X,那么当我读出它时,最好是X的列表或可以向上转换为X的事物列表,即任何扩展 X
{{1}}
希望这会有所帮助。
答案 3 :(得分:17)
基于Bert F's answer我想解释一下我的理解。
假设我们有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.
添加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.
最后让我们尝试添加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.
但是等等 如果某人决定制作一种新型的柠檬,请为SalquLemon说明
public class SaltyLemon extends Lemon{}
现在,fruitExtendedList可以是Fruit,Melon,WaterMelon或SaltyLemon的列表。
所以,我们的陈述
fruitExtendedList.add(new WaterMelon())
也无效。
基本上我们可以说我们不能向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.
现在让我们尝试从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,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.
现在,让我们尝试从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.
添加WaterMelon类型对象
melonSuperList.add(new WaterMelon())
//This is also valid because of same reason as adding Melon
总结一下,我们可以在melonSuperList中添加Melon或其子类,并且只读取Object类型对象。
答案 4 :(得分:14)
super是下限,extends是上限。
根据http://download.oracle.com/javase/tutorial/extra/generics/morefun.html:
解决方案是使用一种形式 有限的通配符我们还没有看到: 具有下限的通配符。该 句法 ? super T表示未知 类型是T(或T)的超类型 本身;记住那个超类型 关系是反身的)。这是双重的 我们去过的有界通配符 使用,我们在哪里使用?将T扩展为 表示一个未知的类型 T的子类型。
答案 5 :(得分:9)
我想看一下区别。假设我们有:
class A { }
class B extends A { }
class C extends B { }
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> |
|-------------------------|-------------------|---------------------------------|
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> |
|-------------------------|-------------------|-------------------------------------------|
在所有情况下:
Object
,而不考虑通配符。null
到可变列表。答案 6 :(得分:3)
使用 extends ,您只能从该集合中获取。你不能投入其中。另外,虽然 super 允许get和put,但get期间的返回类型是?超级T 。
答案 7 :(得分:3)
将项目添加到列表中:
<强>列表&LT; ? extends X&gt; 不允许在列表中添加除null
之外的任何内容。
<强>列表&LT; ? super X&gt; 允许添加任何X(X或其子类型)或null。
从列表中获取项目:
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
答案 8 :(得分:2)
这里最令人困惑的是,无论我们指定什么类型限制,分配只能以一种方式运行:
baseClassInstance = derivedClassInstance;
您可能会认为Integer extends Number
和Integer
将<? extends Number>
作为<? extends Number> cannot be converted to 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>
}
}
(也就是人类用语, 扩展数字的任何内容都可以转换为Integer ):
hs.set(i);
Integer
没问题,因为 Number
可以转换为Integer
的任何超类(而不是因为Number
是Object
的超类1}},这不是真的。)
EDIT添加了关于Consumer Extends和Producer Super的评论 - 它们没有意义,因为它们相应地指定了 nothing 和 {{1}} 。建议您记住PECS,因为CEPS永远不会有用。
答案 9 :(得分:2)
您可以查看上述所有答案,了解为什么.add()
仅限于'<?>'
,'<? extends>'
,部分限于'<? super>'
。
但如果您想记住它,并且不想每次都去探索答案,那么这就是结论:
List<? extends A>
表示这将接受List
的任何A
和A
的子类。
但是您无法在此列表中添加任何内容。甚至不是A
类型的对象。
List<? super A>
表示这将接受A
的任何列表和A
的超类。
您可以添加A
类型的对象及其子类。
答案 10 :(得分:1)
通用通配符针对两个主要需求:
从通用集合中读取 插入通用集合 使用通用通配符定义集合(变量)有三种方法。这些是:
List<?> listUknown = new ArrayList<A>();
List<? extends A> listUknown = new ArrayList<A>();
List<? super A> listUknown = new ArrayList<A>();
List<?>
表示键入未知类型的列表。这可以是List<A>
,List<B>
,List<String>
等。
List<? extends A>
表示作为class A
或subclasses of A
(例如B和C)实例的对象列表。
List<? super A>
表示该列表已键入A class
或superclass of A
。
了解详情:http://tutorials.jenkov.com/java-generics/wildcards.html
答案 11 :(得分:1)
何时使用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将车辆纳入估值师。 知道扩展边界比超级更常见是有用的。
答案 12 :(得分: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>();
`List<? extends X> listvar;`
您可以使用此功能接受方法参数中的列表,并对键入X 执行任何操作(注意:您可以仅从列表中读取X类型的对象 )。
`List<? super Number> listvar;
您可以使用此功能接受方法参数中的列表,并对类型对象执行任何操作,因为您可以仅从列表中读取对象类型的对象。 但是,是的,此处还有一件事是,您可以将X类型的对象添加到列表中。
答案 13 :(得分:0)
例如, 继承顺序假定为O> S> T> U> V
使用扩展关键字
正确:
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();
Object.add(new T()); //error
但是为什么会出错? 让我们看一下List对象初始化的可能性
List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();
如果我们使用Object.add(new T());那么只有
List<? extends T> Object = new List<T>();
但是还有另外两种可能性
列表对象=新的List(); 列表对象=新的List(); 如果我们尝试将(new T())添加到上述两种可能性,则将产生错误,因为T是U和V的上一类。我们尝试向类型U和V的List添加一个T对象[[new T())]。较高级别的对象(基类)不能传递给较低级别的对象(子类)。
由于这两种可能性,即使您使用正确的可能性,Java也会给您带来错误,因为Java不知道您指的是什么对象。因此,您不能将对象添加到List Object = new List();因为存在无效的可能性。
添加对象: 列表对象=新的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.add(new 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()) 列表对象=新的List(); 和 列表对象=新的List(); 然后会给出错误 这是因为 我们不能将T对象[这是new T()]添加到List Object = new List();中。因为它是类型U的对象。我们不能向U Object添加T对象[这是新的T()],因为T是基类,而U是子类。我们不能将基类添加到子类中,这就是发生错误的原因。对于另一种情况也是如此。