2010-09-15 63 views
14

为什么此代码无法编译(Parent是一个接口)?无法为通用类型的Java集合添加值

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) 

回答

36

这是为了安全起见。试想一下,如果它的工作:

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>的意思是”为某种类型的延伸Parent的列表我们不知道哪种类型的 - 它可能是一个List<Parent>,一个List<Child>,或者List<GrandChild>。 “这使其能够安全List<T>的API获取任何项目,从T转换为Parent,但它不是安全的打电话List<T> API转换从ParentT ...因为转换可能是无效的。

+0

多态性的不好的例子有效的调用,我觉得呢? “孩子”不是(总是)“父母”。 – justhalf 2017-03-24 07:14:10

+0

@justhalf:'Parent'在问题中,并且暗示'Child'是子类的自然例子。不是我从头开始选择的类名,而是根据问题的背景来清楚。 – 2017-03-24 08:21:40

+0

是的,我明白了,但我认为如果此处的示例使用“SingleParent”或“NewParent”作为子类,则会更好=) – justhalf 2017-03-24 08:41:26

16
List<? super Parent> 

PECS - “Producer - Extends,Cosumer - Super”。您的ListParent对象的使用者。

1

这是我的理解。

假设我们有2种方法

type L<T> 
    T get(); 
    void set(T); 

假设一个泛型类型,我们有一个超类型P,它有子类型C1, C2 ... Cn。 (为方便起见,我们说P是其自身的子类型,实际上是对Ci之一)

现在,我们也得到了ň具体类型L<C1>, L<C2> ... L<Cn>,仿佛我们已经手写ñ类型:

type L_Ci_ 
    Ci get(); 
    void set(Ci); 

我们不必手动编写它们,这就是要点。有这些类型

L<Ci> oi = ...; 
L<Cj> oj = oi; // doesn't compile. L<Ci> and L<Cj> are not compatible types. 

对于C++模板之间没有关系,这就是故事的结尾。它基本上是宏扩展 - 基于一个“模板”类,它产生了许多具体的类,它们之间没有类型关系。

对于Java,还有更多。我们也得到了一个类型L<? extends P>,它是任何L<Ci>

L<Ci> oi = ...; 
L<? extends P> o = oi; // ok, assign subtype to supertype 

超级型L<? extends P>应该存在什么样的方法?作为一种超级类型,它的任何方法都必须由它的子类型来支持。这种方法将工作:

type L<? extends P> 
    P get(); 

因为在任何亚型L<Ci>的,有一个方法Ci get(),这与P get()兼容 - 压倒一切的方法具有相同的签名和协变返回类型。

这不能set()工作,虽然 - 我们无法找到一个类型X,使void set(X)可以通过void set(Ci)任何Ci覆盖。因此set()方法在L<? extends P>中不存在。

也有一个L<? super P>这是另一种方式。它有set(P),但没有get()。如果SiP的超级类型,L<? super P>L<Si>的超级类型。

type L<? super P> 
    void set(P); 

type L<Si> 
    Si get(); 
    void set(Si); 

set(Si)“覆盖” set(P)不是通常意义上的,但是编译器可以看到,set(P)任何有效的调用是set(Si)

+0

有关set()在此上下文中被拒绝的最佳解释。 – 2012-03-08 16:24:52

相关问题