背景
文接上回,说到过Java的泛型擦除问题,这块我又联想到一个有意思的考点泛型的协变和逆变。
一、协变
首先Java的数组是协变的,所以假设A是B的父类,那么A[]是可以保存A或者B的对象的,并且A[]是B[]的父类。
<? extends T>
,写谁都会写,子类型限定。
相信大家也都见到过JDK中很多源码也有这么用,但为什么要这么写呢?
根因是Java的泛型没有协变类型,无法关联起来,也就没有关系。
利用通配符<? extends T>
,?代表子类,T为父类。
常见的例子比如,List<? extends Fruit> list = new ArrayList<Banana>();
实现向上转型,父类作为变量的申明,只能get(具体子类需强转),不能set。
二、逆变
<? super T>
,超类型限定。
逆变同样也是在各类源码中层出不穷,结合协变的理解,这块相信大家应该不难理解。
常见的例子比如,List<? super Apple> list = new ArrayList<Fruit>();
实现向下转型,子类作为变量的申明,只能set,不能get(只能放在Object对象)。
三、PECS
即Producer Extends,Consumer Super.
通俗理解生产者为协变,消费者为逆变。
针对于生产者,可取,有上界;针对于消费者,可存,有下界。
理解它,也可以通过Java的继承关系,
假设存在继承关系Object-》T、T-》A、T-》B
;
即T为A、B的父类,协变面向子类;逆变面向Object,它是所有对象的父类。
小结
1、协变、逆变的区别要分清。
2、另外,再提一点泛型和通配符的区别,当然也可以结合第一点理解,
<T extends AAA>
用于定义泛型类和方法,擦除后为AAA类型;
<? extends AAA>
用于声明方法的形参,接收AAA和其子类型。