无法为通用types的Java集合添加值
为什么这个代码不能编译( Parent
是一个接口)?
List<? extends Parent> list = ... Parent p = factory.get(); // returns concrete implementation list.set(0, p); // fails here: set(int, ? extends Parent) cannot be applied to (int, Parent)
这是为了安全起见。 想象一下,如果它的工作:
List<Child> childList = new ArrayList<Child>(); childList.add(new Child()); List<? extends Parent> parentList = childList; parentList.set(0, new Parent()); Child child = childList.get(0); // No! It's not a child! Type safety is broken...
List<? extends Parent>
的含义 我们不知道哪个types – 它可能是List<Parent>
, List<Child>
或List<GrandChild>
。 这可以安全地从 List<T>
API中取出任何项目并将其从T
转换为Parent
,但是调用List<T>
API从Parent
转换到T
…是不安全的,因为该转换可能是无效。
List<? super Parent>
PECS – “生产者 – 延伸,消费者 – 超级”。 您的List
是Parent
对象的使用者。
这是我的理解。
假设我们有一个2种方法的genericstypes
type L<T> T get(); void set(T);
假设我们有一个超typesP
,它有子typesC1, C2 ... Cn
。 (为了方便起见,我们说P
是自己的一个子types,实际上就是其中的一个)
现在我们也得到了n个具体typesL<C1>, L<C2> ... L<Cn>
,就好像我们手工写了n个types:
type L_Ci_ Ci get(); void set(Ci);
我们不必手动编写它们,这就是要点。 这些types之间没有关系
L<Ci> oi = ...; L<Cj> oj = oi; // doesn't compile. L<Ci> and L<Cj> are not compatible types.
对于C ++模板,这是故事的结尾。 基本上是macros扩展 – 基于一个“模板”类,它产生了许多具体的类,它们之间没有types关系。
对于Java,还有更多。 我们也有一个typesL<? extends P>
L<? extends P>
,它是任何L<Ci>
的超types
L<Ci> oi = ...; L<? extends P> o = oi; // ok, assign subtype to supertype
什么样的方法应该存在于L<? extends P>
L<? extends P>
? 作为一个超级types,其任何方法都必须由其子types来调用。 这种方法将工作:
type L<? extends P> P get();
因为在它的任何子typesL<Ci>
,都有一个方法Ci get()
,它与P get()
兼容 – 重写方法具有相同的签名和协变返回types。
这不能用于set()
虽然我们找不到一个typesX
,所以对于任何Ci
, void set(Ci)
都可以覆盖void set(X)
。 因此set()
方法在L<? extends P>
中不存在L<? extends P>
L<? extends P>
。
还有一个L<? super P>
L<? super P>
这是另一种方式。 它已经set(P)
,但没有get()
。 如果Si
是P
一个超types, L<? super P>
L<? super P>
是L<Si>
的超级types。
type L<? super P> void set(P); type L<Si> Si get(); void set(Si);
set(Si)
“覆盖” set(P)
不是通常意义上的,但编译器可以看到set(P)
上的任何有效调用都是set(Si)
上的有效调用,