首页 > 代码库 > 一筐梨子&一筐水果——协变性(covariant)
一筐梨子&一筐水果——协变性(covariant)
如果突然看见这个问题,我们经常会想当然。一个梨子是水果,一筐梨子是一筐水果吗?
public class CovariantDemo{ public static void main(String[] args) { Sub[] b = new Sub[10]; Sup[] p = b; p[0] = new Sup(); b[0].m(); } }有什么问题?
b是一筐梨子,p是一筐水果,现在你把一个苹果放在了p中。
所以,编译器认为正确,运行时java.lang.ArrayStoreException: typeSystem.generics.Sup
Java泛型中最令人头痛的是参数化类型的协变性问题。数组的一个重要性质——协变性(covariant):如果A是B的子类,则A[]是B[]的子类型。
然而对于泛型C<T>,参数化类型C<Object>与C<String>无关。这违反人们的直观认知。怎样说服自己:C<Object>与C<String>无关是合理的。
(1)参数化类型C<梨子>保证当前有一筐梨子;而C<水果>则保证当前有一筐水果,可以向一筐水果中放入苹果、菠萝。如果C<水果>持有C<梨子>引用却不导致编译错误的话,就可以向一筐梨子中放入苹果、菠萝,那么泛型的作用——保证/限定泛型中元素类型的作用将荡然无存。(技术原因:类型参数的擦除)
(2)虽然数组具有协变性,但是在使用时,程序员要自律地限制元素类型、需要做强制类型转换、或者需要忍受运行时异常而非编译时错误。而这些正是Java泛型要避免的,Java泛型比Java数组使用起来更安全。另一方面,因为能够方便地使用有限定性的数组如“梨子[]”,数组的协变性不被经常使用,也就不显得讨厌。
(修复例程问题:p[0] = new Sub(); )
A是B的子类型,参数化类型C<A> 与C<B>无协变性。在某些情况下,却需要参数化类型能够协变,注意,协变指参数化类型之间的协变。为此,Java提供通配符(wildcard),包括只读通配符“? extends”和只写通配符“?super”。【5.4.2】
一筐梨子&一筐水果——协变性(covariant)