协变 逆变
协变:covariance
逆变:contravariance
我们假设有class A , class B,其中A为B的超类,f表示变换关系。
f是协变的,当且仅当f(A)是f(B)的超类
f是逆变的,当且仅当f(A)是f(B)的子类
f是不变的,当且仅f(A)和f(B)无任何继承关系
泛型
代码编译完成后,编译器会丢弃类型参数的类型信息;因此,此类型信息在运行时不可用。
泛型的子类型变换
List<Parent> p = new ArrayList<>();
List<Child> c = new ArrayList<>();
虽然Parent类是Child类的父类,但List p和List c并没有继承关系。
由于Set和Set没有任何的继承关系,因此用Set类引用setC是错误的,由于泛型是static checking,因此会在编译阶段报错。
数组的子类型化
如图,Integer是Number的子类,因而Integer[ ]就是Number[ ]的子类。对于Java而言,数组是协变的。
代码擦除:type erasure
类型擦除就是在运行期间,泛型的结构中填写的类型将全部被替换为其上限类型,即extends后的内容(个人理解,就是规定好的能够兼容这个类的“最高父类”),若没有指定上限类型,则将全部被替换为Object。即在编译期间,Set和Set类型是不同的,而在运行期间,类型是相同的。如下图所示。
通配符
通配符?
1、Set<? extends A> setE
setE的泛型就是A的任何一个子类
2、Set<? super B> setS
setS的泛型就是B的任何一个超类
当类更多用来被读取时,用extends
当类更多用来被写入时,用super
这是因为,对于Set<? extends A> setE ,从SetE中读取的任何元素都可以被A类接受,但是,无法确定写入的是具体的哪个类(及其它的子类)。
此外,Set<? extends Parent> 是 Set的父类
Set<? extends Parent> 是 Set<? extends Child>的父类
总结一下extends A 的用法,? extends A表示所存储类型都是A及其子类,但是获取元素所使用的引用类型只能是T或者其父类。使用上限通配符实现向上转型,但是会失去存储对象的能力。上限通配符为集合的协变表示
对于Set<? Super A> setS ,向SetS中写入的任何元素都可以是A的子类,因为setS中存的元素只能是A与它的超类。但是,一旦要从setS中读出元素,无法确定读出的元素类型,只能知道一定可以用Object类去接受它们,一旦用Object去接受这些类,具体的类就会变得没有意义。因而不适合去读出数据。
此外,Set<? super Parent> 是 Set<? super Child>的子类
下限通配符 ? super T表示 所存储类型为T及其父类,但是添加的元素类型只能为T及其子类,而获取元素所使用的类型只能是Object,因为Object为所有类的基类。下限通配符为集合的逆变表示。