学习笔记:逆变与协变

概念

假设Orange类是Fruit类的子类,以集合类List<T>为例:

  1. 协变(covariance):满足条件诸如List<Orange>List<? extends Fruit>的子类型时,称为协变。
  2. 逆变(covariance):满足条件List<Fruit>List<? super Orange>的子类型时,称为逆变。

举例

协变

协变可以理解为父类型向子类型转换:
        要求越来越具体specific
通常用于返回值类型:返回值类型不变或变得更具体
/**
 * 1.定义一个类型上界限定为Fruit的List,即协变
 */
List<? extends Fruit> fruits = new ArrayList<>();

/**
 * 2.编译器报错,不能添加任何类型的数据
 * 原因是:
 * List.add(T t)函数通过上面的类型指定后,参数会变成
 * <? extends Fruit>,从这个参数中,编译器无法知道需要哪个具体的Fruit子类型,
 * Orange、Banana甚至Fruit都可以,因此,为了保证类型安全,编译器拒绝任何类型。
 */
//fruits.add(new Orange());
//fruits.add(new Fruit());
//fruits.add(new Object());

/**
 * 3.此处正常!! 由于我们定义是指定了上界为Fruit,因此此处的返回值肯定至少是Fruit类型,
 * 而基类型可以引用子类型
 */
Fruit f = fruits.get(0);

 逆变

逆变可以理解为子类型向父类型转换:
        要求越来越宽泛specific
通常用于参数类型:参数要向相反(父类)方向的变化,要不变或越来越抽象
/**
 * 1.定义一个Object的List,作为原始数据列表
 */
List<Object> objects = new ArrayList<>();
objects.add(new Object()); //添加数据没有问题
objects.add(new Orange()); //仍然没有问题,

/**
 * 2.定义一个类型下界限定为Fruit的List,并将objects赋值给它。
 * 此时编译不会报错,因为满足逆变的条件。
 */
List<? super Fruit> fruits = objects;

/**
 * 3.add(T t)函数,编译器不会报错,因为fruits接受Fruit的基类类型,
 * 而该类型可以引用其子类型(多态性)
 */
fruits.add(new Orange());
fruits.add(new Fruit());
fruits.add(new RedApple());

/**
 * 4.此处编译器报错,因为fruits限定的是下界是Friut类型,因此,
 * 编译器并不知道确切的类型是什么,没法找到一个合适的类型接受返回值
 */
Fruit f = fruits.get(0);

/**
 * 5.此处不会报错,因为Object是Fruit的最顶层基类,满足下界的限定
 */
//Object obj = fruits.get(0);

参考

Java泛型的协变、逆变和不变 - 简书 (jianshu.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值