IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 泛型的逆变与协变 --《JAVA编程思想》 57 -> 正文阅读

[Java知识库]泛型的逆变与协变 --《JAVA编程思想》 57

我们知道导出类可以向上转型为基类,如:Food 为 Fruit 的基类,则以下代码成立。

 Food food = new Fruit();

假设 Food 和 Fruit 同为 Container 类的泛型参数时,它们之间无法执行类型转换。

尽管持有 Fruit 的 Container 和 持有 Food 的 Container 两者之间存储的泛型参数有继承关系,但从容器类 Container 的角度出发,两个容器之间并无任何关联。

public class Food {}
public class Fruit extends Food{}
public class Container<T> {

    T item;

    public Container(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
    
}
public class ContainerTest {

    public static void main(String[] args) {
        Container<Fruit> fruitContainer = new Container<Fruit>(new Fruit());
        //无法通过编辑
        //Container<Food> foodContainer = fruitContainer;
    }

}

不过我们有时就是想在容器中存储的泛型之间建立继承关系,该怎么办呢?咱们接着往下看。

public class Food {}
public class Meat extends Food {}
public class Fruit extends Food {}
public class Apple extends Fruit {}
public class RedApple extends Apple {}

public class GreenApple extends Apple {}

上述类之间的继承关系如下图。
在这里插入图片描述

想在持有泛型的容器类之间建立继承关系,有两种方式,分别是协变逆变

根据上述各类之间的继承关系举例,<? extends Fruit> 可以帮助我们将泛型参数向上转换,此时泛型的边界为继承自 Fruit 类的任何类,可以是 Fruit ,也可以是 Apple 或者是 RedApple ,但具体是哪一种类型,无法确定,这种情形被称为协变,协变定义了泛型的上界

    public static void main(String[] args) {
        Container<? extends Fruit> fruitContainer = new Container<>();
        fruitContainer=new Container<Apple>();
        fruitContainer=new Container<RedApple>();
        //无法通过编译
        //fruitContainer=new Container<Food>();
    }

在这里插入图片描述

但协变后的泛型会失去写能力。这是因为无法确定<? extends Fruit> 中持有的是哪种具体类型,我们仅仅知道它源于 Fruit,很显然是无法将 GreenApple 塞入 RedApple 的容器中。

不过读能力不会受到影响,因为知道泛型的上界,便可以安全的向上转型为 Fruit 进行返回。

    public static void main(String[] args) {
        Container<? extends Fruit> fruitContainer = new Container<>();
        fruitContainer = new Container<Apple>();
        Fruit item = fruitContainer.getItem();
        //无论通过编译
        //fruitContainer.setItem(new Apple());
    }

协变相反的是逆变逆变能确定的泛型的下界<? super Fruit> 表示持有的都是 FruIt 或者 Fruit 的超类。

逆变会保留容器的写能力,丧失读能力

我们往容器内放入 Fruit 及其子类肯定是安全的,它们都可以向上转型为 Fruit。但是无法放入 Food ,因为 Food 可不一定是由 Fruit 转化而来,可能是 Meat ,故无法放入容器。

在读取容器持有类型时,因不能确定其上界,故只能将存储的元素作为 Object 类型取出,无法读取原本的具体类型。
在这里插入图片描述

    public static void main(String[] args) {
        Container<? super Fruit> fruitContainer = new Container<>();
        fruitContainer = new Container<Food>();
        fruitContainer.setItem(new Apple());
        fruitContainer.setItem(new RedApple());
        //无法通过编译
        //fruitContainer.setItem(new Food());
        Object item = fruitContainer.getItem();
    }

小结
1.协变用于定义泛型的上界,泛型失去写能力,变为只读。
2.逆变用于定义泛型的下界,泛型失去读能力,变为只写。

本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。

若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-02 14:30:32  更:2021-10-02 14:33:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 18:36:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码