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>希望这可以帮助。