List<? super T>
和List<? extends T>
?
我曾经用过List<? extends T>
,但是它不允许我在列表中添加元素list.add(e)
,而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
读取哪种类型的对象:
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
或子类T
。
super
现在考虑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
读取时,可以保证接收什么类型的对象:
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
可能指向ArrayList<Integer>
。 记住PECS : “生产者扩展,消费者超级” 。
“生产者扩展” - 如果需要一个List
来产生T
值(要从列表中读取T
),则需要使用? extends T
进行声明? extends T
,例如List<? extends Integer>
。但是您不能添加到此列表。
“Consumer Super” - 如果您需要一个使用T
值的List
(您想将T
写入列表中),则需要使用? super T
进行声明? super T
,例如List<? super Integer>
。但是不能保证您可以从该列表中读取什么类型的对象。
如果既需要读取列表又要写入列表,则需要完全不使用通配符来声明它,例如List<Integer>
。
注意Java Generics FAQ 中的这个例子 。请注意,源列表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));
}
}
想象一下拥有这种等级制度
通过写
List<? extends C2> list;
您说list
将能够引用(例如) ArrayList
类型的对象,该对象的通用类型是C2
的 7 个子类型之一(包括C2
):
new ArrayList<C2>();
,(可以存储 C2 或子类型的对象)或new ArrayList<D1>();
,(可以存储 D1 或子类型的对象)或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>();
等等。
通过写
List<? super C2> list;
您说list
将能够引用类型(例如) ArrayList
的对象,该对象的泛型类型是C2
的 7 个超类型之一(包括C2
):
new ArrayList<A1>();
,(可以存储 A1 或子类型的对象)或new ArrayList<A2>();
,(可以存储 A2 或子类型的对象)或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(红色)组以图形表示
如您所见,这里有每种情况共有的七个安全类型 : C2
, D1
, D2
, E1
, E2
, E3
, E4
。
list.add(new C2(){});
因为无论我们引用的是哪种列表,都允许使用C2
list.add(new D1(){});
因为无论我们引用的是哪种列表,都允许使用D1
等等。您可能已经注意到,这些类型对应于从C2
类型开始的层次结构。
如果您想进行一些测试,这里是完整的层次结构
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 写入到一个列表,则该列表必须是 A X 的列表或一个事物的名单,我的 X 可以向上转型为我写他们即 X 的任何超 ...
List<? super X>
如果我得到一个列表,并且想从该列表中读取一个 X,则最好是 X 列表或在我读出它们时可以向上投射到 X 的事物列表,即扩展 X 的任何东西
List<? extends X>
希望这可以帮助。