问题
118882328和List<? extends T>
和有什么区别?
我曾经使用过List<? extends T>
,但它不允许我向它添加元素list.add(e)
,而List<? super T>
则允许。
#1 热门回答(1048 赞)
###延伸
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读取什么类型的对象:你可以读取一个数字,因为可以分配给foo3的任何列表都包含Number或Number的子类。你无法读取整数,因为foo3可能指向List <Double>。你无法读取Double,因为foo3可能指向List <Integer>。
- 写入 - 鉴于上述可能的分配,你可以向List foo3添加哪种类型的对象,这对于上述所有可能的ArrayList赋值都是合法的:你不能添加整数,因为foo3可能指向List <Double>。你不能添加Double,因为foo3可能指向List <Integer>。你无法添加数字,因为foo3可能指向List <Integer>。
你不能添加任何对象到List<? extends T>
因为你不能保证什么样的List
it真正指向,所以你不能保证该对象在theList
中被允许。唯一的"保证"是你只能读取它,你将获得aT
或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读取时,你保证会接收哪种类型的对象:由于foo3可能指向List <Number>或List <Object>,因此无法保证整数。你无法保证数字,因为foo3可能指向List <Object>。唯一的保证是你将获得一个Object的实例或Object的子类(但你不知道是什么子类)。
- 写入 - 鉴于上述可能的赋值,你可以向List foo3添加哪种类型的对象,这对于上述所有可能的ArrayList赋值都是合法的:你可以添加一个Integer,因为在上述任何列表中都允许使用Integer。你可以添加Integer子类的实例,因为在上述任何列表中都允许使用Integer子类的实例。你不能添加一个Double,因为foo3可能指向一个ArrayList <Integer>。你无法添加数字,因为foo3可能指向ArrayList <Integer>。你无法添加Object,因为foo3可能指向ArrayList <Integer>。
PECS
RememberPECS:"Producer Extends,Consumer Super"。
- "Producer Extends" - 如果你需要一个List来产生T值(你想从列表中读取Ts),你需要声明它吗?扩展T,例如名单<?扩展整数>。但你无法添加到此列表中。
- "Consumer Super" - 如果你需要一个List消耗T值(你想把Ts写入列表),你需要声明它吗?超级T,例如名单<?超级整数>。但是无法保证你可以从此列表中读取哪种类型的对象。
- 如果你需要同时读取和写入列表,则需要在没有通配符的情况下完全声明它,例如:列表<Integer>的。
###例子
Notethis example from the Java Generics FAQ。请注意源listsrc
(生成列表)如何使用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));
}
}
另见How can I add to List<? extends Number> data structures?
#2 热门回答(165 赞)
想象一下这个层次结构
#1。扩展
通过写作
List<? extends C2> list;
你是说list
将能够引用类型的对象(例如)ArrayList
,其泛型类型是7子类型之一ofC2
(C2
included):
- C2:new ArrayList <C2>();,(可以存储C2或子类型的对象)或
- D1:new ArrayList <D1>();,(可以存储D1或子类型的对象)或
- D2:new ArrayList <D2>();,(可以存储D2或子类型的对象)或......
等等。七种不同的情况:
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>();
等等。
#2。超级
通过写作
List<? super C2> list;
你是说41414919将能够引用类型的对象(例如)ArrayList
,其泛型类型是7超类型之一ofC2
(C2
included):
- A1:new ArrayList <A1>();,(可以存储A1或子类型的对象)或
- A2:new ArrayList <A2>();,(可以存储A2或子类型的对象)或
- A3:new ArrayList <A3>();,(可存储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
对于每种可能的情况,我们有一组"可存储"类型:此处以图形方式表示7(红色)集
正如你所看到的,这里我们有sevensafe类型,每个案例都是常见的:C2
,D1
,D2
,E1
,E2
,E3
,E4
。
- 你可以list.add(new C2(){});因为,无论我们引用哪种List,都允许使用C2
- 你可以list.add(new D1(){});因为,无论我们引用哪种List,都允许使用D1
等等。你可能注意到这些类型对应于从typeC2
开始的层次结构。
#Notes
如果你想进行一些测试,这里是完整的层次结构
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{}
#3 热门回答(50 赞)
我喜欢@Bert F的答案,但这是我大脑看到的方式。
我手里拿着一个X.如果我想将83985671my写入一个List,那么该List需要是一个X列表或一个我的X可以向上编写的事物列表,因为我将它写入,即any超类of X ...
List<? super X>
如果我得到一个List并且我想要从该列表中读取an X,那么最好是X的列表或者可以在我读出它时向上转换为X的列表,即扩展的任何内容X
List<? extends X>
希望这可以帮助。