区别? 超级T>和<? 在Java中扩展T>
List<? super T>
什么区别List<? super T>
List<? super T>
和List<? extends T>
List<? extends T>
?
我曾经使用List<? extends T>
List<? extends T>
,但它不允许我添加到list.add(e)
元素,而List<? super T>
List<? super T>
。
extends
List<? extends Number> foo3
的通配符声明 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
读取什么types的对象:- 您可以读取一个
Number
因为任何可以分配给foo3
的列表都包含一个Number
或Number
的子类。 - 你不能读
Integer
因为foo3
可能指向一个List<Double>
。 - 你不能读一个
Double
因为foo3
可能指向一个List<Integer>
。
- 您可以读取一个
-
写作 – 给定上述可能的赋值,可以将
List foo3
types的对象添加到List foo3
,这对于所有上述可能的ArrayList
赋值是合法的:- 你不能添加一个
Integer
因为foo3
可能指向一个List<Double>
。 - 你不能添加一个
Double
因为foo3
可能指向一个List<Integer>
。 - 你不能添加一个
Number
因为foo3
可能指向一个List<Integer>
。
- 你不能添加一个
你不能添加任何对象到List<? extends T>
List<? extends T>
因为你不能保证它真正指向什么样的List
,所以你不能保证对象被允许在List
。 唯一的“保证”是你只能读取它,你会得到一个T
或T
子类。
super
现在考虑一下List <? super T>
List <? super T>
。
List<? super Integer> foo3
的通配符声明 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
读取时,你保证接收什么types的对象:- 你不能保证一个
Integer
因为foo3
可能指向一个List<Number>
或者List<Object>
。 - 你不能保证一个
Number
因为foo3
可能指向一个List<Object>
。 - 唯一的保证是你将得到一个
Object
或Object
子类的实例(但你不知道是什么子类)。
- 你不能保证一个
-
写作 – 给定上述可能的赋值,可以将
List foo3
types的对象添加到List foo3
,这对于所有上述可能的ArrayList
赋值是合法的:- 您可以添加一个
Integer
因为Integer
是允许在任何上述列表中。 - 您可以添加一个
Integer
子类的实例,因为Integer
子类的一个实例在以上任何列表中都是允许的。 - 你不能添加一个
Double
因为foo3
可能指向一个ArrayList<Integer>
。 - 你不能添加一个
Number
因为foo3
可能指向一个ArrayList<Integer>
。 - 你不能添加一个
Object
因为foo3
可能指向一个ArrayList<Integer>
。
- 您可以添加一个
佩奇
记住PECS : “生产者延伸,消费者超级” 。
-
“Producer Extends” – 如果你需要一个
List
来产生T
值(你想从列表中读取T
s),你需要用? extends T
? extends T
,例如List<? extends Integer>
List<? extends Integer>
。 但是你不能添加到这个列表。 -
“消费者超级” – 如果你需要一个
List
来消费T
值(你想把T
s写入列表),你需要用? super T
? super T
,例如List<? super Integer>
List<? super Integer>
。 但是不能保证你可以从这个列表中读取什么types的对象。 -
如果你需要读取和写入列表,你需要声明它完全没有通配符,例如
List<Integer>
。
例
请注意Javagenerics常见问题中的这个例子 。 注意源列表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)); } }
另请参阅如何添加到列表<? 扩展Number>数据结构?
想象一下这个层次结构
1.延长
通过写作
List<? extends C2> list;
你是说list
将能够引用一个types的对象(例如),其genericstypes是C2
(包括C2
)的7 个子types之一的ArrayList
:
- C2:
new ArrayList<C2>();
,(可以存储C2或子types的对象)或 - D1:
new ArrayList<D1>();
,(可以存储D1或子types的对象)或 - D2:
new ArrayList<D2>();
,(可以存储D2或子types的对象)或…
等等。 七种不同的情况:
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
对于每种可能的情况,我们都有一套“可存储”types:这里用graphics表示7(红色)
正如你所看到的,没有一种安全的types是每个案件都通用的:
- 你不能
list.add(new C2(){});
因为它可能是list = new ArrayList<D1>();
- 你不能
list.add(new D1(){});
因为它可能是list = new ArrayList<D2>();
等等。
超级
通过写作
List<? super C2> list;
你是说list
将能够引用一个types的对象(例如),其genericstypes是C2
的七种超types之一(包括C2
):
- A1:
new ArrayList<A1>();
,(可以存储A1或者子types的对象)或者 - A2:
new ArrayList<A2>();
,(可以存储A2或者子types的对象)或者 - A3:
new ArrayList<A3>();
,(可以存储A3或子types的对象)或…
等等。 七种不同的情况:
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
对于每种可能的情况,我们都有一套“可存储”types:这里用graphics表示7(红色)
正如你所看到的,在这里我们有七种安全types ,每种情况都是共同的: C2
, D1
, D2
, E1
, E2
, E3
, E4
。
- 你可以
list.add(new C2(){});
因为无论我们引用的List是什么types,C2
都是允许的 - 你可以
list.add(new D1(){});
因为无论我们引用的List是什么types,D1
都是允许的
等等。 您可能已经注意到,这些types对应于从C2
types开始的层次结构。
笔记
这里是完整的层次,如果你想做一些testing
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,即任何超类的X …
List<? super X>
如果我得到一个列表,并且想从列表中读出一个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.
最后让我们尝试添加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.
但是等等 ,如果有人决定让一种新型的柠檬让说为SaltyLemon为理由
public class SaltyLemon extends Lemon{}
现在fruitExtendedList可以是Fruit,Melon,WaterMelon或者SaltyLemon的列表。
所以,我们的发言
fruitExtendedList.add(new WaterMelon())
也是无效的。
基本上我们可以说,我们不能写任何东西到一个fruitExtendedList。
这总结了List<? extends Fruit>
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或任何其他物体都不能被读取。
但请注意,我们可以读取对象types实例
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中设置一些值。
添加对象types对象
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.
添加水果types的对象
melonSuperList.add(new Fruit()) //This is also not valid as melonSuperList can be list of Melon
添加Melontypes对象
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.
添加WaterMelontypes的对象
melonSuperList.add(new WaterMelon()) //This is also valid because of same reason as adding Melon
总结起来,我们可以在melonSuperList中添加Melon或其子类,并只读Object对象。
超级是一个下界,扩展是一个上界。
根据http://download.oracle.com/javase/tutorial/extra/generics/morefun.html :
解决scheme是使用我们还没有看到的有界通配符的forms:下界的通配符。 语法? super T表示一个未知types,它是T(或T本身;超types关系是自反的)超types。 这是我们使用的有界通配符的对偶,我们在哪里使用? 扩展T表示一个未知types,它是T的一个子types
使用扩展你只能从集合中获得。 你不能把它放进去。 另外,虽然super允许get和put,get中的返回types是? 超级T。 详细的解释在我的博客http://preciselyconcise.com/java/generics/c_wildcards.php
这里最令人困惑的是,无论我们指定什么types的限制,分配只有一个方面:
baseClassInstance = derivedClassInstance;
你可能会认为Integer extends Number
, Integer
会作为一个<? extends Number>
<? extends Number>
,但是编译器会告诉你<? extends Number> cannot be converted to Integer
<? extends Number> cannot be converted to Integer
(也就是说,用人们的说法, 任何扩展的数字都可以转换为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(i);
是可以的,因为Integer
可以转换为Number
任何超类 (并且不是因为Integer
是Number
的超类,这不是真的)。
编辑添加了关于消费者扩展和生产者超级评论 – 他们没有意义,因为他们相应地指定什么 ,只是Object
。 build议您记住PECS,因为CEPS是无用的。
列表<? 扩展X>不允许添加任何东西到列表中。
列表<? 超X>允许添加任何是X(X或其子类)。
何时使用扩展和超级
通配符在方法参数中非常有用。 它们为方法接口提供了必要的灵活性。
人们经常感到困惑的时候使用扩展和何时使用超界。 经验法则是取得原则。 如果你从参数化的容器中获得一些东西,请使用extends。
int totalFuel(List<? extends Vehicle> list) { int total = 0; for(Vehicle v : list) { total += v.getFuel(); } return total;}
方法totalFuel从列表中获取交通工具,询问他们有多less燃料,并计算总计。 如果将对象放入参数化的容器中,请使用super。
int totalValue(Valuer<? super Vehicle> valuer) { int total = 0; for(Vehicle v : vehicles) { total += valuer.evaluate(v); } return total;}
方法totalValue将车辆放入估值器。 知道扩展绑定比超级更普遍是有用的。
通用通配符针对两个主要需求:
从generics集合中读取插入generics集合有三种方法可以使用generics通配符来定义集合(variables)。 这些是:
List<?> listUknown = new ArrayList<A>(); List<? extends A> listUknown = new ArrayList<A>(); List<? super A> listUknown = new ArrayList<A>();
List<?>
表示键入未知types的列表。 这可以是List<A>
, List<B>
, List<String>
等
List<? extends A>
List<? extends A>
表示作为class A
实例的对象的列表,或者subclasses of A
(例如B和C)。 List<? super A>
List<? super A>
意味着列表被input到A class
或superclass of A
的superclass of A
。
阅读更多: http : //tutorials.jenkov.com/java-generics/wildcards.html
你可以通过上面的所有答案来理解为什么.add()
被限制为'<?>'
, '<? extends>'
'<? extends>'
,部分为'<? super>'
'<? super>'
。
但是,如果你想记住这一切,那么这就是所有的结论,并且不想每次都去探索答案:
List<? extends A>
List<? extends A>
意味着这将接受A
任何List
和A
子类。 但是你不能添加任何东西到这个列表中。 甚至不是typesA
对象。
List<? super A>
List<? super A>
意味着这将接受A
和超类的任何列表。 您可以添加A
typesA
对象及其子类。