Java中泛型 型变 (协变 逆变)

本篇主要介绍 Java 中的 泛型 型变 当子类型关系出现在更加复杂类型中时,新类型中有着怎样的类型关系?

Java中是不允许 子类型的泛型类型对象 赋值给 父类型的泛型类型对象的

Java中泛型 型变 (协变 逆变)

1.概述

先来看看一段代码

    public static void main(String[] args) {
       Integer age = 25;
       Number number = age;
     
       List<Integer> integerList = Arrays.asList(1, 2, 3);
       List<Number> numberList = integerList;
  }

我们都知道 Integer 是 Number的子类  上面代码中会有问题吗? 看着好像没啥问题


但是其实是不可以的

Java中泛型 型变 (协变 逆变)


搞清楚上面的原因 先理解一下 基础类型和实参类型 : 例如对于 List<String>, List 就是基础类型而这里的 String 就是实参类型

然而在Java中 泛型的 实参类型的的父子关系 是不会传递到 泛型的 基础类型的

List<Integer> integerList = Arrays.asList(1, 2, 3);
// 也就是说 虽然 Integer是Number的子类 但是 List<Integer> 并不是 List<Number>的子类
List<Number> numberList = integerList; //所以编译器直接抛错了


又比如 有如下方法, 接受 List<Number>类型  但是却只能传递

// 又比如 有入
public static void printList(List<Number> numberList) {
 numberList.forEach(System.out::println);
}

public static void main(String[] args) {
 List<Integer> integerList = Arrays.asList(1, 2, 3);
 printList(integerList);//编译器抛错! 但是其实我们确实需要实现啊
}


2.如何解决

思考一下上面的的问题, 该如何解决呢? 其实就引入了 Java中的 泛型 型变的知识, 在Java中也称为 上下界

  • 上限 :  <? extends 类 >

  • 下限:   <? super 类 >


//这样就不会抛错了!!  
public static void printList(List<? extends Number> numberList) {
 numberList.forEach(System.out::println);
}

public static void main(String[] args) {
 List<Integer> integerList = Arrays.asList(1, 2, 3);
 //这样就不会抛错了!!
 List<? extends Number> numberList = integerList;

 printList(integerList);
}


2.2 上限 (泛型 协变)

上限 :  <? extends 类 >   也称为 泛型 协变

有了它就可以 规定  泛型的基础类型的父子关系 从  实参类型来推倒

协变限制

当这样操作后 会带来协变的限制 就是在只允许取值操作 , 不允许 添加操作

Java中泛型 型变 (协变 逆变)

你仔细想想  确实呢, 我可能给了你一个 List<Double>  但是你却想往里面添加 Integer类型 肯定是不行的, 所以 使用了 协变 则 只能获取 不能添加, 就算你添加 Object 也不行


2.3 下限( 泛型 逆变)

下限:   <? super 类 >   也称为 泛型 逆变

前面我们都在看 协变的样例 下面来看看 逆变


static class Animal {

}
static class Dog extends Animal {}

List<Animal> animalList = Arrays.asList(new Animal(),new Animal());
List<Dog> dogList = animalList; //这里思考一下 好像肯定不行

// 肯定心想 List<Animal> 想被赋 List<Dog> 都不行 还得借助 协变 , 还想反过来? 哈哈


但是当我 添加了 ? super 直接就可以成功赋值了, 这就是 逆变, 它是把 实参类型的父子关系 直接反过来了 , 也就是说 现在 List<Animal> 是List<Dog> 的子类了 这就是 逆变

Java中泛型 型变 (协变 逆变)


逆变限制

协变有限制, 逆变也有限制, 它会限制 只能去添加 不能去获取, 因为你并不知道获取的类型是 什么 , 因为它没有上限了 但是在Java中是可以 调用遍历方法 只不过都是Object类型


总结

本篇主要介绍了 Java 中的泛型的 基础类型的实参类型 以及它们的关系,总结如下:

  • Java中泛型的 实参类型 如果存在父子关系 但是 泛型的 基础类型 并不会拥有这种关系 如Integer 是 Number的子类 但是 List<Integer> 和 List<Number> 并没有关系 也不存在父子关系

  • 要想把 泛型的 实参类型 传递到 泛型的基础类型中 需要有 协变 和 逆变 2种方式 就是 ? extends 和 ? super

  • 协变 ? extends :  上限  List<? extends Number> numberList = doubleList 存在的限制就是 无法添加 只能获取元素

  • 逆变 ? super :      下限 List<? super Dog> dogList = animalList; 存在的限制就是 无法获取具体类型元素 只能获取到Object类型,只能添加元素, 可以添加 下限和下限的子类 如上就是 Dog 和 Dog 的子类`



原文始发于微信公众号(Johnny屋):Java中泛型 型变 (协变 逆变)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/89725.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!