为什么此代码无法编译(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)
为什么此代码无法编译(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)
这是为了安全起见。试想一下,如果它的工作:
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转换从Parent
到T
...因为转换可能是无效的。
List<? super Parent>
PECS - “Producer - Extends,Cosumer - Super”。您的List
是Parent
对象的使用者。
这是我的理解。
假设我们有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()
。如果Si
是P
的超级类型,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)
有关set()在此上下文中被拒绝的最佳解释。 – 2012-03-08 16:24:52
多态性的不好的例子有效的调用,我觉得呢? “孩子”不是(总是)“父母”。 – justhalf 2017-03-24 07:14:10
@justhalf:'Parent'在问题中,并且暗示'Child'是子类的自然例子。不是我从头开始选择的类名,而是根据问题的背景来清楚。 – 2017-03-24 08:21:40
是的,我明白了,但我认为如果此处的示例使用“SingleParent”或“NewParent”作为子类,则会更好=) – justhalf 2017-03-24 08:41:26