无法为通用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 – “生产者 – 延伸,消费者 – 超级”。 您的ListParent对象的使用者。

这是我的理解。

假设我们有一个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 ,所以对于任何Civoid set(Ci)都可以覆盖void set(X) 。 因此set()方法在L<? extends P>中不存在L<? extends P> L<? extends P>

还有一个L<? super P> L<? super P>这是另一种方式。 它已经set(P) ,但没有get() 。 如果SiP一个超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)上的有效调用,