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 - 鉴于上述可能的分配,您保证从 List foo3 读取什么类型的对象:
您可以读取 Number ,因为可以分配给 foo3 的任何列表都包含 Number 或 Number 的子类 .
您无法读取 Integer 因为 foo3 可能指向 List<Double> .
您无法读取 Double 因为 foo3 可能指向 List<Integer> .
Writing - 鉴于上述可能的分配,您可以向 List foo3 添加哪种类型的对象,这对于 all 上述可能的 ArrayList 赋值是合法的:
您无法添加 Integer ,因为 foo3 可能指向 List<Double> .
您无法添加 Double ,因为 foo3 可能指向 List<Integer> .
您无法添加 Number ,因为 foo3 可能指向 List<Integer> .
您无法向 List<? extends T> 添加任何对象,因为您无法保证它实际指向的是哪种 List ,因此您无法保证该对象在该 List 中被允许 . 唯一的"guarantee"是你只能读取它,你将获得 T 或 T 的子类 .
超级
现在考虑 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
Reading - 鉴于上述可能的分配,当您从 List foo3 读取时,您保证会接收哪种类型的对象:
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));
}
}
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(红色)集
如您所见,并非每种情况都有一种安全类型:
你不能 list.add(new C2(){}); 因为它可能是 list = new ArrayList<D1>();
你不能 list.add(new D1(){}); 因为它可能是 list = new ArrayList<D2>();
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
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.
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.
Basically we can say that we cannot write anything to a fruitExtendedList.
This sums up 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.
添加Fruit类型对象
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
To sum it up we can add Melon or its subclass in melonSuperList and read only Object type object.
解决方案是使用一种我们尚未见过的有界通配符形式:具有下限的通配符 . 语法? super T表示一个未知类型,它是T的超类型(或T本身;记住超类型关系是自反的) . 这是我们使用的有界通配符的双重性,我们在哪里使用?扩展T表示一个未知类型,它是T的子类型 .
6
我想想象一下这种差异 . 假设我们有:
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> |
|-------------------------|-------------------|-------------------------------------------|
在所有情况下:
无论通配符如何,您都可以从列表中始终 getObject .
无论通配符如何,您都可以始终 addnull 到一个可变列表 .
3
使用 extends ,您只能从集合中获取 . 你不能投入其中 . 此外,虽然 super 允许get和put,但get期间的返回类型是 ? super T .
14
这里最令人困惑的是,无论我们指定什么类型限制,赋值只能以一种方式工作:
baseClassInstance = derivedClassInstance;
您可能认为 Integer extends Number 和 Integer 将作为 <? extends Number> ,但编译器会告诉您 <? 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 的超类,这不是真的) .
List<? extends A> 表示这将接受 List 的 A 和 A 的子类 . 但是您无法在此列表中添加任何内容 . 甚至不是 A 类型的对象 .
List<? super A> 表示这将接受 A 的任何列表和 A 的超类 . 您可以添加 A 类型的对象及其子类 .
0
最高投票的答案涵盖了许多方面的细节 . 但是,我会尝试以不同的方式回答这个问题 .
我们需要考虑两件事,
1.分配给列表变量
List<? extends X> listvar;
这里,任何 list of X or list of subclasses of X can be assigned 到listvar .
List<? extends Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Integer>();
List<? super X> listvar;
这里,任何 list of X or list of superclasses of X can be assigned 到listvar .
List<? super Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Object>();
2.对列表变量执行读取或写入操作
`List<? extends X> listvar;`
您可以使用此功能接受方法参数中的列表,并对 type X 执行任何操作(注意:您可以从列表中 only read objects of type X ) .
`List<? super Number> listvar;
您可以使用此功能接受方法参数中的列表,并在 type Object 上执行任何操作,因为您可以从列表中选择 only read objects of type Object . But yes, additional thing here is , you can add objects of type X into the list.
2
List< ? extends X > 不允许在列表中添加除null之外的任何内容 .
List< ? super X > 允许添加任何-X(X或其子类)或null .
62
例如,继承顺序假定为O> S> T> U> V.
Using extends Keyword ,
Correct:
List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();
InCorrect:
List<? extends T> Object = new List<S>();
List<? extends T> Object = new List<O>();
super Keyword:
Correct:
List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();
InCorrect:
List<? super T> Object = new List<U>();
List<? super T> Object = new List<V>();
添加对象:List Object = 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.add(new T());那么只有当它是正确的
List<? extends T> Object = new List<T>();
但还有两种可能性
List Object = new List(); List Object = new List();如果我们尝试将(new T())添加到上述两种可能性中,它将产生错误,因为T是U和V的优等级 . 我们尝试将一个T对象[new(new T())]添加到U和V类型的List中 . 较高的类对象(基类)不能传递给较低的类Object(Sub类) .
由于额外的两种可能性,即使你使用正确的可能性,Java也会给你错误,因为Java不知道你指的是什么对象 . 所以你不能将对象添加到List Object = new List();因为有可能无效 .
添加对象:List Object = 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.add(new T());只有以下可能性,
List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();
如果我们试图在List Object = new List()中使用Object.add(new T());和List Object = new List();然后它会给出错误这是因为我们不能将T对象[这是新的T()]添加到List Object = new List();因为它是U型的对象 . 我们不能将一个T对象[它是新的T()]添加到U对象,因为T是基类而U是子类 . 我们不能将基类添加到子类,这就是错误发生的原因 . 对于另一个案例,这是相同的 .
0
|
v
列表<?延伸X>?可以是X的任何子类或其自身X. List <?超级X>?可以是X的任何超类或其自身X.
15 回答
延伸
List<? extends Number> foo3
的通配符声明表示其中任何一个都是合法的分配:Reading - 鉴于上述可能的分配,您保证从
List foo3
读取什么类型的对象:您可以读取 Number ,因为可以分配给
foo3
的任何列表都包含Number
或Number
的子类 .您无法读取
Integer
因为foo3
可能指向List<Double>
.您无法读取
Double
因为foo3
可能指向List<Integer>
.Writing - 鉴于上述可能的分配,您可以向
List foo3
添加哪种类型的对象,这对于 all 上述可能的ArrayList
赋值是合法的:您无法添加
Integer
,因为foo3
可能指向List<Double>
.您无法添加
Double
,因为foo3
可能指向List<Integer>
.您无法添加
Number
,因为foo3
可能指向List<Integer>
.您无法向
List<? extends T>
添加任何对象,因为您无法保证它实际指向的是哪种List
,因此您无法保证该对象在该List
中被允许 . 唯一的"guarantee"是你只能读取它,你将获得T
或T
的子类 .超级
现在考虑
List <? super T>
.List<? super Integer> foo3
的通配符声明表示其中任何一个都是合法的分配:Reading - 鉴于上述可能的分配,当您从
List foo3
读取时,您保证会接收哪种类型的对象:您无法保证
Integer
因为foo3
可能指向List<Number>
或List<Object>
.您无法保证
Number
因为foo3
可能指向List<Object>
.only 保证您将获得 Object 的实例或
Object
的子类(但您不知道是什么子类) .Writing - 鉴于上述可能的分配,您可以向
List foo3
添加哪种类型的对象,这对于 all 上述可能的ArrayList
赋值是合法的:您可以添加
Integer
,因为上述任何列表中都允许使用Integer
.您可以添加
Integer
的子类的实例,因为在上述任何列表中都允许使用Integer
的子类的实例 .您无法添加
Double
,因为foo3
可能指向ArrayList<Integer>
.您无法添加
Number
,因为foo3
可能指向ArrayList<Integer>
.您无法添加
Object
,因为foo3
可能指向ArrayList<Integer>
.PECS
记住PECS: "Producer Extends, Consumer Super" .
"Producer Extends" - 如果您需要
List
来生成T
值(您希望从列表中读取T
s),则需要使用? extends T
声明它,例如:List<? extends Integer>
. 但您无法添加到此列表中 ."Consumer Super" - 如果您需要
List
来消耗T
值(您希望将T
写入列表中),则需要使用? super T
声明它,例如:List<? super Integer>
. 但是无法保证您可以从此列表中读取哪种类型的对象 .如果您需要同时读取和写入列表,则需要在没有通配符的情况下完全声明它,例如
List<Integer>
.示例
注意this example from the Java Generics FAQ . 请注意源列表
src
(生成列表)如何使用extends
,目标列表dest
(使用列表)使用super
:另见How can I add to List<? extends Number> data structures?
想象一下这个层次结构
1.延伸
通过写作
你是说
list
将能够引用类型的对象(例如)ArrayList
,其泛型类型是 subtypes 的 subtypes 之一(包括C2
):C2:
new ArrayList<C2>();
,(可以存储C2或子类型的对象)或D1:
new ArrayList<D1>();
,(可以存储D1或子类型的对象)或D2:
new ArrayList<D2>();
,(可以存储D2或子类型的对象)或......等等 . 七种不同的情况:
对于每种可能的情况,我们有一组“可存储”类型:此处以图形方式表示7(红色)集
如您所见,并非每种情况都有一种安全类型:
你不能
list.add(new C2(){});
因为它可能是list = new ArrayList<D1>();
你不能
list.add(new D1(){});
因为它可能是list = new ArrayList<D2>();
等等 .
2.超级
通过写作
你是说
list
将能够引用类型的对象(例如)ArrayList
,其泛型类型是C2
的之一(包括
C2
):A1:
new ArrayList<A1>();
,(可以存储A1或子类型的对象)或A2:
new ArrayList<A2>();
,(一个对象可以存储A2或子类型)或A3:
new ArrayList<A3>();
,(可存储A3或子类型的对象)或......等等 . 七种不同的情况:
对于每种可能的情况,我们有一组“可存储”类型:此处以图形方式表示7(红色)集
如您所见,这里我们有七种安全类型,每种情况都是通用的:
C2
,D1
,D2
,E1
,E2
,E3
,E4
.你可以
list.add(new C2(){});
因为,无论我们引用哪种List,都允许C2
你可以
list.add(new D1(){});
因为,无论我们引用哪种List,都允许D1
等等 . 您可能已经注意到这些类型对应于从
C2
类型开始的层次结构 .注意事项
如果您想进行一些测试,这里是完整的层次结构
我喜欢@Bert F的答案,但这是我大脑看到的方式 .
我手里拿着一个X.如果我想将 write 我的X放入一个List,那么该List必须是X的List或者我可以将其编写为X的事物列表,因为我将它们写入,即任何 superclass 的X ...
如果我得到一个List并且我希望 read 是该列表中的X,那么最好是X的列表或者当我读出它时可以向上转换为X的事物列表,即任何 extends X
希望这可以帮助 .
基于Bert F's answer我想解释一下我的理解 .
假设我们有3个 class
我们在这里
好的,现在让我们尝试从fruitExtendedList获取一些值
再来试试吧
情况也是如此
现在让我们尝试在fruitExtendedList中设置一些对象
添加水果对象
添加Melon对象
最后让我们尝试添加WaterMelon对象
But wait 如果有人决定制作一种新型的柠檬,可以说是为了争论SaltyLemon
现在fruitExtendedList可以是Fruit,Melon,WaterMelon或SaltyLemon的列表 .
所以,我们的声明
也无效 .
Basically we can say that we cannot write anything to a fruitExtendedList.
This sums up List<? extends Fruit>
现在让我们看看
现在让我们尝试从melonSuperList中获取一些值
同样,Melon,WaterMelon或任何其他物体都无法读取 .
但请注意,我们可以读取对象类型实例
现在,让我们尝试从melonSuperList设置一些值 .
添加对象类型对象
添加Fruit类型对象
添加Melon类型对象
添加WaterMelon类型对象
To sum it up we can add Melon or its subclass in melonSuperList and read only Object type object.
super是下限,extends是上限 .
根据http://download.oracle.com/javase/tutorial/extra/generics/morefun.html:
我想想象一下这种差异 . 假设我们有:
List<? extends T>
- 阅读和分配:List<? super T>
- 写作和分配:在所有情况下:
无论通配符如何,您都可以从列表中始终 get
Object
.无论通配符如何,您都可以始终 add
null
到一个可变列表 .使用 extends ,您只能从集合中获取 . 你不能投入其中 . 此外,虽然 super 允许get和put,但get期间的返回类型是 ? super T .
这里最令人困惑的是,无论我们指定什么类型限制,赋值只能以一种方式工作:
您可能认为
Integer extends Number
和Integer
将作为<? extends Number>
,但编译器会告诉您<? extends Number> cannot be converted to Integer
(也就是说,用人的说法,扩展数字的任何内容都可以转换为Integer是错误的):hs.set(i);
是可以的,因为Integer
可以转换为Number
的任何超类(而不是因为Integer
是Number
的超类,这不是真的) .EDIT添加了关于Consumer Extends和Producer Super的评论 - 它们没有意义,因为它们相应地指定了什么,只是
Object
. 建议您记住PECS,因为CEPS永远不会有用 .通用通配符针对两个主要需求:
从泛型集合中读取插入到泛型集合中有三种方法可以使用通用通配符定义集合(变量) . 这些是:
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
When to use extends and super
通配符在方法参数中最有用 . 它们允许方法接口具有必要的灵活性 .
人们经常在使用扩展时和使用超边界时感到困惑 . 经验法则是获取原则 . 如果从参数化容器中获取内容,请使用extends .
方法totalFuel从列表中获取车辆,询问他们有多少燃料,并计算总数 . 如果将对象放入参数化容器中,请使用super .
方法totalValue将车辆放入估价师 . 知道扩展边界比超级更常见是有用的 .
您可以通过上面的所有答案来理解为什么
.add()
被限制为'<?>'
,'<? extends>'
,部分限制为'<? super>'
.但是如果你想记住它,并且不想每次都去探索答案,那么这就是结论:
List<? extends A>
表示这将接受List
的A
和A
的子类 . 但是您无法在此列表中添加任何内容 . 甚至不是A
类型的对象 .List<? super A>
表示这将接受A
的任何列表和A
的超类 . 您可以添加A
类型的对象及其子类 .最高投票的答案涵盖了许多方面的细节 . 但是,我会尝试以不同的方式回答这个问题 .
我们需要考虑两件事,
1.分配给列表变量
List<? extends X> listvar;
这里,任何 list of X or list of subclasses of X can be assigned 到listvar .
List<? extends Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Integer>();
List<? super X> listvar;
这里,任何 list of X or list of superclasses of X can be assigned 到listvar .
List<? super Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Object>();
2.对列表变量执行读取或写入操作
您可以使用此功能接受方法参数中的列表,并对 type X 执行任何操作(注意:您可以从列表中 only read objects of type X ) .
您可以使用此功能接受方法参数中的列表,并在 type Object 上执行任何操作,因为您可以从列表中选择 only read objects of type Object . But yes, additional thing here is , you can add objects of type X into the list.
List< ? extends X > 不允许在列表中添加除null之外的任何内容 .
List< ? super X > 允许添加任何-X(X或其子类)或null .
例如,继承顺序假定为O> S> T> U> V.
Using extends Keyword ,
Correct:
InCorrect:
super Keyword:
Correct:
InCorrect:
添加对象:List Object = new List();
但为什么错误呢?让我们看一下列表对象初始化的可能性
如果我们使用Object.add(new T());那么只有当它是正确的
但还有两种可能性
List Object = new List(); List Object = new List();如果我们尝试将(new T())添加到上述两种可能性中,它将产生错误,因为T是U和V的优等级 . 我们尝试将一个T对象[new(new T())]添加到U和V类型的List中 . 较高的类对象(基类)不能传递给较低的类Object(Sub类) .
由于额外的两种可能性,即使你使用正确的可能性,Java也会给你错误,因为Java不知道你指的是什么对象 . 所以你不能将对象添加到List Object = new List();因为有可能无效 .
添加对象:List Object = new List();
但为什么上述两个错误发生?我们可以使用Object.add(new T());只有以下可能性,
如果我们试图在List Object = new List()中使用Object.add(new T());和List Object = new List();然后它会给出错误这是因为我们不能将T对象[这是新的T()]添加到List Object = new List();因为它是U型的对象 . 我们不能将一个T对象[它是新的T()]添加到U对象,因为T是基类而U是子类 . 我们不能将基类添加到子类,这就是错误发生的原因 . 对于另一个案例,这是相同的 .