List<之间的区别是什么?super T>和List<?延伸T> ?
我曾经使用List<?扩展T>,但它不允许我添加元素到它的List .add(e),而List<?super T>有。
List<之间的区别是什么?super T>和List<?延伸T> ?
我曾经使用List<?扩展T>,但它不允许我添加元素到它的List .add(e),而List<?super T>有。
当前回答
我喜欢来自@Bert F的答案,但这是我大脑看到的方式。
我手里有个X。如果我想把我的X写进一个列表,这个列表需要是一个X的列表,或者是一个我写X时可以向上转换的列表,即X的任何超类…
List<? super X>
如果我得到一个列表,我想从这个列表中读取一个X,那最好是一个X的列表,或者是一个可以在我读取它们时向上转换为X的列表,即任何扩展X的东西
List<? extends X>
其他回答
这里最令人困惑的是,无论我们指定了什么类型限制,赋值只能以一种方式工作:
baseClassInstance = derivedClassInstance;
您可能认为Integer扩展了Number,并且Integer可以作为<?扩展数字>,但是编译器会告诉你<?extends Number>不能转换为Integer(也就是说,在人类的说法中,任何扩展Number的东西都可以转换为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(我);是可以的,因为Integer可以转换为Number的任何超类(而不是因为Integer是Number的超类,这不是真的)。
EDIT添加了一条关于消费者扩展和生产者超级的注释——它们没有意义,因为它们相应地指定了什么,而只是对象。建议您记住PECS,因为CEPS从来都没有用。
想象一下有这样的层次结构
1. 扩展
通过编写
List<? extends C2> list;
你是说,list将能够引用类型为ArrayList的对象,其泛型类型是C2的7个子类型之一(包括C2):
C2: new ArrayList<C2>();,(可以存储C2或子类型的对象)或 D1: new ArrayList<D1>();,(可以存储D1或子类型的对象)或 D2: new ArrayList<D2>();,(可以存储D2或子类型的对象)or…
等等。七种不同的情况:
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个(红色)集
正如你所看到的,并没有一种安全的类型适用于所有情况:
你不能列举。添加(新的C2 () {});因为可以是list = new ArrayList<D1>(); 你不能列举。添加(新D1 () {});因为可以是list = new ArrayList<D2>();
等等。
2. 超级
通过编写
List<? super C2> list;
你是说,list将能够引用类型为ArrayList的对象,其泛型类型是C2的7个超类型之一(包括C2):
A1: new ArrayList<A1>();,(可以存储A1或子类型的对象)或 A2: new ArrayList<A2>();,(可以存储A2或子类型的对象)或 A3: new ArrayList<A3>();,(可以存储A3或子类型的对象)or…
等等。七种不同的情况:
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个(红色)集
正如你所看到的,这里有7种安全类型,在每种情况下都是常见的:C2, D1, D2, E1, E2, E3, E4。
你可以列出。添加(新的C2 () {});因为,不管我们引用的是哪种List, C2都是允许的 你可以列出。添加(新D1 () {});因为,不管我们引用的是哪种List, 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{}
向列表中添加一个项目:
列表< ?extends X >不允许向列表中添加任何东西,除了null。 列表< ?super X >允许添加任何-a X (X或其子类型),或null。
从列表中获取一项:
当你从List< ?扩展X >,你可以将它赋值给X类型的变量或X的任何超类型,包括Object。 当你从List< ?super X >,你只能将它分配给Object类型的变量。
一些例子:
List<? extends Number> list1 = new ArrayList<Integer>();
list1.add(null); //OK
Number n = list1.get(0); //OK
Serializable s = list1.get(0); //OK
Object o = list1.get(0); //OK
list1.add(2.3); //ERROR
list1.add(5); //ERROR
list1.add(new Object()); //ERROR
Integer i = list1.get(0); //ERROR
List<? super Number> list2 = new ArrayList<Number>();
list2.add(null); //OK
list2.add(2.3); //OK
list2.add(5); //OK
Object o = list2.get(0); //OK
list2.add(new Object()); //ERROR
Number n = list2.get(0); //ERROR
Serializable s = list2.get(0); //ERROR
Integer i = list2.get(0); //ERROR
使用只能从集合中获得的扩展。你不能投入进去。此外,虽然超级允许获取和放置,在获取期间的返回类型是?超级T。
Super是下界,extends是上界。
根据http://download.oracle.com/javase/tutorial/extra/generics/morefun.html:
解决方法是使用一种形式的 有界通配符我们还没见过 具有下界的通配符。的 语法呢?超级T表示未知 类型是T(或T 本身;记住超类型 关系是自反的)。它是双重的 有界通配符 使用,我们在哪里使用?将T延伸到 表示未知类型 T的子型。