Java泛型简介

news2024/12/23 22:22:45

参考博客:https://www.jb51.net/article/192850.htm

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

一、泛型类

 泛型中表示继承/实现的写法:

<T extends A>: 样类中的泛型T只能是A接口的实现类,传入非A接口编译会出错。

注意:<T extends A>这里的限定使用关键字extends,后面可以是类也可以是接口。但这里的extends已经不是继承的含义了,应该理解为T类型是实现A接口的类型,或者T是继承了XX类的类型。

 二、extends T和 super T

 

<? extends T> 和 <? super T> 是Java泛型中的“通配符(Wildcards)” 和 “边界(Bounds)”的概念

  • <? extends T> 是指   “上界通配符(Upper Bounds Wildcards)
  • <? super T> 是指  “下界通配符(Lower Bounds Wildcards)

1. 为什么要用通配符和边界?

使用泛型的过程中,经常出现一种很别扭的情况。比如,我们有Fruit类,和它的派生类Apple类。

class Fruit {}

class Apple extends Fruit {}

然后有一个最简单的容器:Plate类。盘子里可以放一个泛型的“东西”。我们可以对这个东西做最简单的“放”和“取”的动作:set()和get()方法。

public class Plate<T> {

    private T item;

    public Plate(T t) {

        this.item = t;

    }

    public T get() {

        return item;

    }

    public void set(T item) {

        this.item = item;

    }

}

现在我定义一个“水果盘子”,逻辑上水果盘子当然可以装苹果。

Plate<Fruit> p=new Plate<Apple>(new Apple());

但实际上Java编译器不允许这个操作。会报错,“装苹果的盘子”无法转换成“装水果的盘子”。

Type mismatch: cannot convert from Plate<Apple> to Plate<Fruit>

所以我的尴尬症就犯了。实际上,编译器脑袋里认定的逻辑是这样的:

  • 苹果 IS-A 水果
  • 装苹果的盘子 NOT-IS-A 装水果的盘子

所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系的。所以我们不可以把Plate<Apple>的引用传递给Plate<Fruit>。

为了让泛型用起来更舒服,Sun的大脑袋们就想出了<? extends T>和<? super T>的办法,来让“水果盘子” 和 “苹果盘子”之间发生关系。

2. 什么是上界?

下面代码就是“上界通配符(Upper Bounds Wildcards)”:

Plate<extends Fruit>

翻译成人话就是:一个能放水果以及一切是水果派生类的盘子。再直白点就是:啥水果都能放的盘子。这和我们人类的逻辑就比较接近了。Plate<? extends Fruit>和Plate<Apple>最大的区别就是:Plate<? extends Fruit>是Plate<Fruit>以及Plate<Apple>的基类。直接的好处就是,我们可以用“苹果盘子”给“水果盘子”赋值了。

Plate<? extends Fruit> p=new Plate<Apple>(new Apple());

如果把Fruit和Apple的例子再扩展一下,食物分成水果和肉类,水果有苹果和香蕉,肉类有猪肉和牛肉,苹果还有两种青苹果和红苹果。

//Lev 1

class Food{}

//Lev 2

class Fruit extends Food{}

class Meat extends Food{}

//Lev 3

class Apple extends Fruit {}

class Banana extends Fruit{}

class Pork extends Meat{}

class Beef extends Meat{}

//Lev 4

class ReadApple extends Apple{}

class GreenApple extends Apple{}

在这个体系中,上界通配符 “Plate<? extends Fruit>” 覆盖下图中蓝色的区域

 

 3. 什么是下界?

 相对应的,“下界通配符(Lower Bounds Wildcards)”: 

Plate<super Fruit>

表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子。Plate<? super Fruit>是Plate<Fruit>的基类,但不是Plate<Apple>的基类。对应刚才那个例子,Plate<? super Fruit>覆盖下图中红色的区域。

 

4. 上下界通配符的副作用

边界让Java不同泛型之间的转换更容易了。但不要忘记,这样的转换也有一定的副作用。那就是容器的部分功能可能失效。

还是以刚才的Plate为例。我们可以对盘子做两件事,往盘子里set( )新东西,以及从盘子里get( )东西。

public class Plate<T> {

    private T item;

    public Plate(T t) {

        this.item = t;

    }

    public T get() {

        return item;

    }

    public void set(T item) {

        this.item = item;

    }

}

4.1 上界<? extends T>不能往里存,只能往外取

<? extends Fruit>会使往盘子里放东西的set()方法失效。但取东西get()方法还有效。比如下面例子里两个set()方法,插入Apple和Fruit都报错。

Plate<? extends Fruit> fruitPlate = new Plate<Apple>(new Apple());

//不能存入任何元素

fruitPlate.set(new Apple());    //Error  编译错误

fruitPlate.set(new Fruit());    //Error  编译错误

//读取出来的东西只能放在Fruit或它的基类里

Fruit newFruit1=fruitPlate.get();

Object newFruit2 = fruitPlate.get();

Apple newFruit3 = fruitPlate.get();  //Error

原因是编译器只知道容器内是Fruit或者它的派生类,但具体是什么类型不知道。可能是Fruit?可能是Apple?也可能是Banana,RedApple,GreenApple?编译器在看到后面用Plate<Apple>赋值以后,盘子里没有被标上有“苹果”。而是标上一个占位符:CAP#1,来表示捕获一个Fruit或Fruit的子类,具体是什么类不知道,代号CAP#1。然后无论是想往里插入Apple或者Meat或者Fruit编译器都不知道能不能和这个CAP#1匹配,所以就都不允许。

所以通配符<?>和类型参数<T>的区别就在于,对编译器来说所有的T都代表同一种类型。比如下面这个泛型方法里,三个T都指代同一个类型,要么都是String,要么都是Integer。

public <T> List<T> fill(T... t);

但通配符<?>没有这种约束,Plate<?>单纯的就表示:盘子里放了一个东西,是什么我不知道。

 4.2 下界<? super T>不影响往里存,但往外取只能放在Object对象里

使用下界<? super Fruit>会使从盘子里取东西的get( )方法部分失效,只能存放到Object对象里。set( )方法正常。 

Plate<? super Fruit> fruitPlate = new Plate<Fruit>(new Fruit());

//存入元素正常

fruitPlate.set(new Apple());

fruitPlate.set(new Fruit());

//读取出来的东西只能存放在Object类里

Object newFruit1 = fruitPlate.get();

Fruit newFruit2 = fruitPlate.get();  //Error

Apple newFruit3 = fruitPlate.get();  //Error

因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Fruit的基类,那往里存粒度比Fruit小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失。

 5. PECS原则

最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了:

  • 频繁往外读取内容的,适合用上界Extends。
  • 经常往里插入的,适合用下界Super。

三、泛型的协变与逆变

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/77453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vuex持久化

下载&#xff1a; vuex-persistedstate npm install --save vuex-persistedstate 或者&#xff1a; npm install --save vuex-persistedstate --legacy-peer-deps 引入使用&#xff1a;在store文件夹下的index文件中&#xff0c;也就是放vuex的js代码中引入&#xff1a; i…

周末福利 | 21天学通Python完整版,豆瓣评分9.6!

前言 又到了周末啦&#xff0c;小编例行给大家发福利&#xff01; 今天福利的内容是21天学通Python完整版&#xff0c;这是一本豆瓣评分9.6的人工智能入门书籍&#xff01;全面、系统、深入地讲解了Python编程基础语法与高级应用。在讲解过程中&#xff0c;通过大量实际操作的…

mac m1 安装docker docker 安装php 5.6 和 7.2 避坑指南

通过该link可以下载兼容mac m1核心的docker-desktop: Docker Desktop - Docker 所有基于amd64架构核心的都加上--platform linux/amd64来运行,这样就能愉快的玩耍了: docker search centos7 php56 #搜索 docker images #显示所有镜像 docker pull sglim2/centos7 建议使用ln…

如何让青少年在AI时代抢占先机

点击蓝字关注我们AI TIME欢迎每一位AI爱好者的加入&#xff01;11月17日&#xff0c;由智谱AI支持&#xff0c;北京市科委、中关村管委会科普专项经费资助的系列栏目“科普大佬说”很荣幸邀请到国家科技部“新一代人工智能创新发展与应用研究”项目、中小学人工智能教育服务平台…

在VSCode中用Markdown自动生成PPT详细使用指南,支持多种风格和排版,支持ppt转为pdf等

在VSCode中用Markdown自动生成PPT详细使用指南&#xff0c;支持多种风格和排版&#xff0c;支持ppt转为pdf等。 Marp 官网&#xff1a; https://marp.app/ 这款软件也能在 Windows 或 Linux 系统上使用&#xff0c;并不局限于 macOS 系统。 今天要介绍的是 Marp 推出的 VS Cod…

PLC-Recorder实现速度高达0.24ms准确周期采集的方法(带时间戳采集)

目录 1、PLC的发送程序 2、PLC连接配置 3、PLC-Recorder侧的通讯设置 4、PLC-Recorder的通道配置 5、PLC-Recorder的变量配置 6、正常通讯情况的界面 7、记录数据的情况 8、小结 如果要以非常高的速度高速采集各种控制器&#xff08;典型的是PLC&#xff09;的数据&…

【Linux】gcc/g++

目录 Linux编译器-gcc/g使用 No.1 背景知识 No.2 预处理(进行宏替换) No.3 编译&#xff08;生成汇编&#xff09; No.4 汇编&#xff08;生成机器可识别代码&#xff09; No.5 动态链接过程 No.6 gcc选项 Linux编译器-gcc/g使用 No.1 背景知识 预处理&#xff08;进行…

什么是Serverless?

Serverless 无服务器&#xff1b;无主机&#xff1b; Serverless &#xff0c;按中文翻译&#xff0c;称为「无服务器」。被认为是新一代的云计算发展方向。 在某些场景可以解读为一种软件系统架构方法&#xff0c;通常称为 Serverless 架构 关于 Serverless 的定义&#xf…

留学Assignment写作怎么注意论证方法?

Assignment的论证方法&#xff0c;对于一篇Assignment而言&#xff0c;学会如何论证&#xff0c;对Assignment的逻辑和内容&#xff0c;有一定的作用。掌握常用的论证方法&#xff0c;了解Assignment要求&#xff0c;在Assignment写作的过程中&#xff0c;学会加以应用&#xf…

VMware 安装、移除Ubuntu系统

目录 前言 安装虚拟机 1. 新建虚拟机 2. 进入向导 3. 选择系统镜像 4. 添加系统设置&#xff0c;用户名主机名密码 <实际没啥用...在系统安装过程中才设置的> 5. 设置虚拟机名称&#xff0c;存放位置 6. 磁盘容量 7. 设置虚拟机硬件配置 8. 自定义硬件 9. 开机…

如何实现jwt鉴权机制?

一、是什么 JWT&#xff08;JSON Web Token&#xff09;&#xff0c;本质就是一个字符串书写规范&#xff0c;如下图&#xff0c;作用是用来在用户和服务器之间传递安全可靠的信息 在目前前后端分离的开发过程中&#xff0c;使用token鉴权机制用于身份验证是最常见的方案&…

Unity 发布安卓包的配置

unity版本是&#xff1a;unity5.6.7&#xff0c;主要是我现在工作中只会在一些工业软件中会用到3d部分&#xff0c;所以版本低了一些&#xff0c;感觉低版本打开unity的速度反而更快一些。发布这个帖子主要目的是 Android SDK 可选项太多了&#xff0c;不熟悉的话&#xff0c;根…

数据技术篇之数据同步

第3章 数据同步 1.数据同步基础 直连同步 &#xff08;1&#xff09;什么是直连同步&#xff1f;直连同步是指通过定义好的规范接口 API 和基于动态链接库的方式直接连接业务库&#xff0c;如 ODBC/JDBC 等规定了统 一规范的标准接口&#xff0c;不同的数据库基于这套标准接口…

gnulib源码安装

接上文《autoconf-archive源码安装》&#xff0c; 链接如下&#xff1a; autoconf-archive源码安装_蓝天居士的博客-CSDN博客 上篇文章在构建autoconf-archive的时候&#xff0c;第一步引导构建就出现了错误&#xff0c;如下所示&#xff1a; $ ./bootstrap.sh ./bootstrap…

SSM+Mysql实现的大学校园兼职系统(功能包含注册登录,发布兼职、个人中心、论坛交流、系统公告、查看兼职信息、查看用户信息、私聊等)

博客目录SSMMysql实现的大学校园兼职系统实现功能截图系统功能使用技术代码完整源码SSMMysql实现的大学校园兼职系统 本系统是一个在线的大学校园兼职系统&#xff0c;商家可以在上面发布自己的兼职&#xff0c;学生可以根据自己的需要&#xff0c;联系兼职&#xff0c;给学生…

JavaScript 中如何实现并发控制?

一、并发控制简介 在日常开发过程中&#xff0c;你可能会遇到并发控制的场景&#xff0c;比如控制请求并发数。那么在 JavaScript 中如何实现并发控制呢&#xff1f;在回答这个问题之前&#xff0c;我们来简单介绍一下并发控制。 假设有 6 个待办任务要执行&#xff0c;而我们…

Kubernetes HPA 动态弹性扩缩容

1.HPA 1.1HPA介绍 1.在Kubernetes中&#xff0c;HPA自动更新工作负载资源&#xff08;例如&#xff1a;Deployment或者StatefulSet&#xff09;&#xff0c;目的是自动扩缩工作负载以满足需求&#xff0c;水平扩缩意味着对增加的负载的响应是部署更多的 Pod&#xff0c;与垂直…

算法工程师深度解构ChatGPT技术

引言 | 本栏目特邀腾讯知名语言文本项目算法工程师冉昱、薛晨&#xff0c;用专业视野带你由浅入深了解ChatGPT技术全貌。它经历了什么训练过程&#xff1f;成功关键技术是什么&#xff1f;将如何带动行业的变革&#xff1f;开发者如何借鉴ChatGPT思路和技术&#xff0c;投入到日…

西门子PLC S7-1500产生精确时间戳及各种数据处理的方法

目录 1、完整程序 2、将时钟转换成整型数 3、获取相对时间 4、转成微秒&#xff08;μs&#xff09;&#xff0c;并转成32位无符号整数 5、翻转的问题 6、有时间戳采集时的对时机制 在数据采集时&#xff0c;精准的相对时间是非常重要的。尤其是高速采集时&#xff0c;上位…

算法训练第四十五天 | LeetCode 70、322、279背包问题

LeetCode 70爬楼梯 题目简析&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 思路分析&#xff1a; 用完全背包的思路来做&#xff0c;见注释 //普通dppublic int climbStairs(in…