Java泛型的协变和逆变

2023-03-07 09:32:16 浏览数 (1)

背景

文接上回,说到过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和其子类型。

0 人点赞