【Java数据结构】泛型的进阶部分(泛型通配符)
1.❤️❤️前言~🥳🎉🎉🎉
Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。
如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的内容感兴趣,记得关注我👀👀以便不错过每一篇精彩。
当然,如果在阅读中发现任何问题或疑问,我非常欢迎你在评论区留言指正🗨️🗨️。让我们共同努力,一起进步!
加油,一起CHIN UP!💪💪
🔗个人主页:E绵绵的博客
📚所属专栏:1. JAVA知识点专栏
深入探索JAVA的核心概念与技术细节
2.JAVA题目练习
实战演练,巩固JAVA编程技能
3.c语言知识点专栏
揭示c语言的底层逻辑与高级特性
4.c语言题目练习
挑战自我,提升c语言编程能力
📘 持续更新中,敬请期待❤️❤️
借鉴的相关文章:Java 中的泛型(两万字超全详解)_java 泛型-CSDN博客
2.泛型通配符
我们希望泛型能够处理某一类型范围的类型参数,比如某个泛型类和它的子类,为此 Java 引入了泛型通配符这个概念。
泛型通配符有 3 种形式:
- <?> :被称作无限定的通配符。
- <? extends T> :被称作有上界的通配符。
- <? super T> :被称作有下界的通配符。
接下来将分别介绍 3 种形式的泛型通配符。
2.1上界通配符 <? extends T>
<? extends T> 的定义
上界通配符
<? extends T>
:T 代表了类型参数的上界,<? extends T>
表示类型参数的范围是 T 和 T 的子类。需要注意的是:<? extends T>
也是一个数据类型实参,它和 Number、String、Integer 一样都是一种实际的数据类型。
下面给个例子:
public class GenericType {public static void main(String[] args) { ArrayList<Number> list01 = new ArrayList<Integer>();// 编译错误ArrayList<? extends Number> list02 = new ArrayList<Integer>();// 编译正确}
}
我们发现,ArrayList< Integer > 和 ArrayList< Number > 之间不存在继承关系,所以编译错误。而引入上界通配符的概念后,我们便可以用 ArrayList<? extends Number> 接收 ArrayList< Integer > 。
所以这也就意味着ArrayList<? extends T>能接收AraayList<T或T的子类>。
所以综上所述,ArrayList<? extends Number> 可以代表 ArrayList< Integer >、ArrayList< Float >、… 、ArrayList< Number >中的某一个集合,但是我们不能指定 ArrayList<? extends Number> 的数据类型。(这里有点难理解)
public class GenericType {public static void main(String[] args) { ArrayList<? extends Number> list = new ArrayList<>();list.add(new Integer(1));// 编译错误list.add(new Float(1.0));// 编译错误}
}
可以这样理解,ArrayList<? extends Number> 集合表示了:我这个集合可能是 ArrayList< Integer > 集合,也可能是 ArrayList< Float > 集合,… ,还可能是 ArrayList< Number > 集合;但到底是哪一个集合,不能确定;程序员也不能指定。
所以,在上面代码中,创建了一个 ArrayList<? extends Number> 集合 list,但我们并不能往 list 中添加 Integer、Float 等对象,这也说明了 list 集合并不是某个确定了数据类型的集合
思考:那既然 ArrayList<? extends Number> 可以代表 ArrayList< Integer > 或 ArrayList< Float >,为什么不能向其中加入 Integer、Float 等对象呢?
其原因是 ArrayList<? extends Number> 表示的是一个未知类型的 ArrayList 集合,它可以代表 ArrayList< Integer >或 ArrayList< Float >… 等集合,但却不能确定它到底是 ArrayList< Integer > 还是 ArrayList< Float > 集合。
因此,泛型的特性决定了不能往 ArrayList<? extends Number> 集合中加入 Integer 、 Float 等对象,以防止在获取 ArrayList<? extends Number> 集合中元素的时候,产生 ClassCastException 异常。
那为什么还需要引入上界统配符
的概念?---- 答:是为了拓展方法形参中类型参数的范围。下面有个例子:
// 改写前 public class PairHelper {static int addPair(Pair<Number> p) {Number first = p.getFirst();Number last = p.getLast();return first.intValue() + last.intValue();} }// 改写后 public class PairHelper {static int addPair(Pair<? extends Number> p) {Number first = p.getFirst();Number last = p.getLast();return first.intValue() + last.intValue();} }
在改写前,我们无法使 addPair(Pair< Number> p) 方法接收 Pair< Integer > 对象。而在有了上界通配符的概念后,这个问题便有了解决办法,就是将 <Number>改写为<? extends Number>。由于 Pair< Integer > 可以被 Pair<? extends Number>接收 ,所以就能调用 addPair() 方法,我们改写就成功了。
除了可以传入 Pair< Integer > 对象,我们还可以传入 Pair< Double > 对象,Pair< BigDecimal > 对象等等,因为 Double 类和 BigDecimal 类也都是 Number 的子类。
<? extends T> 的用法
上面说到,我们无法确定 ArrayList<? extends Number> 具体是什么数据类型的集合,因此其 add() 方法会受限(即不能往集合中添加任何数据类型的对象);但是可以往集合中添加 null,因为 null 表示任何类型。
那我们该怎么办呢?以下是正确用法
上界通配符 <? extends T> 的正确用法:
public class Test {public static void main(String[] args) {// 创建一个 ArrayList<Integer> 集合ArrayList<Integer> integerList = new ArrayList<>();integerList.add(1);integerList.add(2);// 将 ArrayList<Integer> 传入 printIntVal() 方法printIntVal(integerList);// 创建一个 ArrayList<Float> 集合ArrayList<Float> floatList = new ArrayList<>();floatList.add((float) 1.0);floatList.add((float) 2.0);// 将 ArrayList<Float> 传入 printIntVal() 方法printIntVal(floatList);}public static void printIntVal(ArrayList<? extends Number> list) {// 遍历传入的集合,并输出集合中的元素 for (Number number : list) {System.out.print(number.intValue() + " ");}System.out.println();}
}
在 printIntVal() 方法中,其形参为 ArrayList<? extends Number>,因此,可以给该方法传入 ArrayList< Integer >、ArrayList< Float > 等集合。
需要注意的是:在 printIntVal() 方法内部,必须要将传入集合中的元素赋值给Number 对象,而不能赋值给某个子类对象; 是因为根据 ArrayList<? extends Number> 的特性,并不能确定传入集合的数据类型(即不能确定传入的是 ArrayList< Integer > 还是 ArrayList< Float >)
假设在 printIntVal() 方法中存在下面代码:
Integer intNum = (Integer) number;
若是传入集合为 ArrayList< Float >,则必然会产生ClassCastException 异常。
下界通配符 <? super T> 的错误用法:
public class Test {public static void main(String[] args) {ArrayList<? extends Number> list = new ArrayList();list.add(null);// 编译正确list.add(new Integer(1));// 编译错误list.add(new Float(1.0));// 编译错误}public static void fillNumList(ArrayList<? extends Number> list) {list.add(new Integer(0));//编译错误list.add(new Float(1.0));//编译错误list.set(0, new Integer(2));// 编译错误list.set(0, null);// 编译成功,但不建议这样使用}
}
在 ArrayList<? extends Number> 集合中,不能添加任何数据类型的对象,只能添加空值 null,因为 null 可以表示任何数据类型。
<? extends T> 小结
一句话总结:使用 extends 通配符表示后该数据只可以读,不能写。
2.2下界通配符 <? super T>
<? super T> 的定义
下界通配符 <? super T>:T 代表了类型参数的下界,<? super T>表示类型参数的范围是 T 和 T 的超类,直至 Object。需要注意的是: <? super T> 也是一个数据类型实参,它和 Number、String、Integer 一样都是一种实际的数据类型。
下面给个例子:
public class GenericType {public static void main(String[] args) { ArrayList<Integer> list01 = new ArrayList<Number>();// 编译错误ArrayList<? super Integer> list02 = new ArrayList<Number>();// 编译正确}
}
我们发现,ArrayList< Integer > 和 ArrayList< Number > 之间不存在继承关系,所以编译错误。而引入上界通配符的概念后,我们便可以用 ArrayList<? super Number> 接收 ArrayList< Integer > 。
所以这也就意味
着ArrayList<? super T>能接收AraayList<T或T的父类>。
ArrayList<? super Integer> 只能表示指定类型参数范围中的
某一个集合
,但我们不能指定 ArrayList<? super Integer> 的数据类型。(这里有点跟上界通配符一样难理解)下面请看例子:
public class GenericType {public static void main(String[] args) { ArrayList<? super Number> list = new ArrayList<>();list.add(new Integer(1));// 编译正确list.add(new Float(1.0));// 编译正确// Object 是 Number 的父类 list.add(new Object());// 编译错误} }
这里奇怪的地方出现了,为什么和ArrayList<? extends Number> 集合不同, ArrayList<? super Number> 集合中可以添加 Number 类及其子类的对象呢?
其原因是, ArrayList<? super Number> 的下界是 ArrayList< Number > 。因此,我们可以确定 Number 类及其子类的对象自然可以加入 ArrayList<? super Number> 集合中; 而 Number 类的父类对象就不能加入 ArrayList<? super Number> 集合中了,因为不能确定 ArrayList<? super Number> 集合的数据类型。
<? super T> 的用法
下界通配符 <? super T> 的正确用法:
public class Test {public static void main(String[] args) {// 创建一个 ArrayList<? super Number> 集合ArrayList<Number> list = new ArrayList(); // 往集合中添加 Number 类及其子类对象list.add(new Integer(1));list.add(new Float(1.1));// 调用 fillNumList() 方法,传入 ArrayList<Number> 集合fillNumList(list);System.out.println(list);}public static void fillNumList(ArrayList<? super Number> list) {list.add(new Integer(0));list.add(new Float(1.0));}
}
与带有上界通配符的集合ArrayList<? extends T>只能添加null不同,带有下界通配符的集合ArrayList<? super Number> 中可以添加 Number 类及其子类的对象;ArrayList<? super Number>的下界就是ArrayList<Number>集合,因此,其中必然可以添加 Number 类及其子类的对象;但不能添加 Number 类的父类对象(不包括 Number 类)。
下界通配符 <? super T> 的错误用法:
public class Test {public static void main(String[] args) {// 创建一个 ArrayList<Integer> 集合ArrayList<Integer> list = new ArrayList<>();list.add(new Integer(1));// 调用 fillNumList() 方法,传入 ArrayList<Integer> 集合fillNumList(list);// 编译错误}public static void fillNumList(ArrayList<? super Number> list) {list.add(new Integer(0));// 编译正确list.add(new Float(1.0));// 编译正确// 遍历传入集合中的元素,并赋值给 Number 对象;会编译错误for (Number number : list) {System.out.print(number.intValue() + " ");System.out.println();}// 遍历传入集合中的元素,并赋值给 Object 对象;可以正确编译// 但只能调用 Object 类的方法,不建议这样使用for (Object obj : list) {System.out.println(obj);使用}}
}
注意,ArrayList<? super Number> 代表了 ArrayList< Number >、 ArrayList< Object > 中的某一个集合,而 ArrayList< Integer > 并不属于 ArrayList<? super Number> 限定的范围,因此,不能往 fillNumList() 方法中传入 ArrayList< Integer > 集合。
并且,不能将传入集合的元素赋值给 Number 对象,因为传入的可能是 ArrayList< Object > 集合,向下转型可能会产生ClassCastException 异常。
不过,可以将传入集合的元素赋值给 Object 对象,因为 Object 是所有类的父类,不会产生ClassCastException 异常,但这样的话便只能调用 Object 类的方法了,不建议这样使用。
<? super T> 小结
一句话总结:使用 super 通配符表示可以写,但不能读。
2.3无限定通配符 <?>
我们已经讨论了
<? extends T>
和<? super T>
作为方法参数的作用。实际上,Java 的泛型还允许使用无限定通配符<?>,即只定义一个?
符号。无界通配符
<?>
:?
代表了任何一种数据类,需要注意的是:<?>
也是一个数据类型实参,它和 Number、String、Integer 一样都是一种实际的数据类型。例如ArrayList<?> 可以接收 ArrayList< Integer>、ArrayList< Number >、ArrayList< Object >中的某一个集合。
举例如下:
public class GenericType {public static void main(String[] args) {ArrayList<Integer> list01 = new ArrayList<>(123, 456);ArrayList<?> list02 = list01; // 安全地向上转型}
}
上述代码是可以正常编译运行的,因为 ArrayList<?> 可以接收ArrayList< Integer 》
ArrayList<?> 既没有上界也没有下界,因此,它可以代表所有数据类型的某一个集合,但我们不能指定 ArrayList<?> 的数据类型。
public class GenericType {public static void main(String[] args) {ArrayList<?> list = new ArrayList<>();list.add(null);// 编译正确Object obj = list.get(0);// 编译正确list.add(new Integer(1));// 编译错误Integer num = list.get(0);// 编译错误} }
ArrayList<?> 集合的数据类型是不确定的,因此我们只能往集合中添加 null;而我们从 ArrayList<?> 集合中取出的元素,也只能赋值给 Object 对象,不然会产生
ClassCastException 异常
(原因可以结合上界和下界通配符理解)
3.<? extends T>与<? super T> 对比
(1)对于<? extends 类型>,编译器将只允许读操作,不允许写操作。即只可以取值,不可以设值。
(2)对于<? super 类型>,编译器将只允许写操作,不允许读操作。即只可以设值(比如 set 操作),不可以取值(比如 get 操作)。
以上两点都是针对于源码里涉及到了类型参数的方法而言的。
比如对于 List 而言,不允许的写操作有 add 方法,因为它的方法签名是boolean add(E e);,此时这个形参 E 就变成了一个涉及了通配符的类型参数;而不允许的读操作有 get 方法,因为它的方法签名是E get(int index);,此时这个返回值 E 就变成了一个涉及了通配符的类型参数。
4.总结
所以我们泛型的进阶部分就结束了,把通配符讲完了,我们数据结构部分也就结束了。接下来将学习新的篇章——数据库,数据库会不会开一个新的专栏有待商酌。
在此,我们诚挚地邀请各位大佬们为我们点赞、关注,并在评论区留下您宝贵的意见与建议。让我们共同学习,共同进步,为知识的海洋增添更多宝贵的财富!🎉🎉🎉❤️❤️💕💕🥳👏👏👏
相关文章:

【Java数据结构】泛型的进阶部分(泛型通配符)
1.❤️❤️前言~🥳🎉🎉🎉 Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的…...

大模型实战一、Ollama+RagFlow 部署本地知识库
大模型实战一、OllamaRagFlow 部署本地知识库 参考你提供的文章,这里是基于 Windows 系统通过 Docker 安装部署 RagFlow 和 Ollama 的本地化大模型知识库的详细教程。本文将指导你如何在 Windows 上使用 Docker 来设置 RagFlow 和 Ollama 环境,并安装通…...

系统工程建模MBSE
################################# ############# 片段一 ############## ################################# 下图采用“V”模式显示了集成的基于模型的系统/嵌入式软件开发流程Harmony。左侧描述了自顶向下的设计流程,而右侧显示了自底而上的从单元测试到最终系统验收测试…...

SVN的使用技巧
SVN(Subversion)是近年来崛起的版本管理工具,因为是免费的,所以用的人还是不少的。故做一些总结。 如果是新手,基本对SVN一点都不了解的话,建议去学习一下这个系统的教程,讲的也很详细Tortoise…...

使用 RabbitMQ 实现秒杀订单系统的异步消息处理
使用 RabbitMQ 实现秒杀订单系统的异步消息处理 在秒杀系统中,如何确保高并发环境下的订单处理稳定高效是个很大的挑战。为了解决这个问题,我们通常会引入消息队列,通过异步处理来削峰填谷。这篇文章将详细讲解如何使用 RabbitMQ 来设计一个…...

oracle19.3单机升级到Oracle19.22
1.补丁包、opatch准备 -rw-r--r-- 1 oracle oinstall 1817908992 9月 10 14:25 p35943157_190000_Linux-x86-64.zip -rw-r--r-- 1 oracle oinstall 133535622 9月 10 14:22 p6880880_190000_Linux-x86-64.zip2.解压补丁包和opatch包 先将原有opatch备份 [oraclecyptdg ~]$…...

半导体的发展--创世新产品介绍
文章目录 半导体的发展 半导体的发展 现代社会对于芯片的需求是越来越多了,90 年代我们能在收音机,电视机,DVD,上面看到芯片的身影,进入 2000 年,电脑,手机逐渐进入中国家庭,中国高…...

Ubuntu WSL使用技巧
0 Preface/Foreword 1 默认为root用户 当下载完成Ubuntu之后,首次登录,当完成初始化后,提示输入新的用户名时候,直接点击右上角的X按钮,再重新登陆,系统会默认使用root权限登录。...

4 个步骤带你快速上手 Einstein Copilot for Tableau
如果你的企业仍未部署或希望迁移至 Tableau Cloud,可考虑订阅 Tableau 高级套件。 自 Einstein Copilot for Tableau 发布以来,相信部分用户已经尝试过在 Tableau Cloud 中借助 AI 对话助理,快速解决数据分析中的问题,获得更准确的…...

C++ | Leetcode C++题解之第386题字典序排数
题目: 题解: class Solution { public:vector<int> lexicalOrder(int n) {vector<int> ret(n);int number 1;for (int i 0; i < n; i) {ret[i] number;if (number * 10 < n) {number * 10;} else {while (number % 10 9 || numbe…...

vsftpd配置用户和密码让其他客户端连接
一、第一个主机:vsftpd下载及配置 前置准备: #卸载防火墙 yum -y remove firewalld #为了不让防火墙有影响,iptables配置也清空 iptables -F vim /etc/selinux/conf SELINUXdisabled #主要是把它改为disabled或者permissive SELINUXTYPEtargeted #重启linux让seli…...

Oracle使用序列后提示违反唯一约束---解决办法
1、问题原因分析 出现这个问题的原因是插入数据的时候,由于之前没有使用序列插入,而是直接插入了一个比当前序列nextval还大的值,即直接将id写死了。后面再使用序列插入的时候,如果序列小于该值的话,是可以正常插入的…...

乐观锁悲观锁
乐观锁 乐观锁的核心思想是“尽量不去锁定资源,而是尽量让线程并发地工作”,并在最后阶段检查冲突,只有在检测到冲突时才会采取纠正措施。乐观锁通常通过以下方式实现: 版本号控制:每次对共享资源进行修改时…...

Unity面试:什么是UnityEvent?
UnityEvent是Unity引擎中一种特殊的事件系统,属于Unity的事件和委托机制。它允许开发者在运行时定义和管理事件的响应,从而实现松耦合的事件处理。 以下是UnityEvent的一些主要特点和用途: 松耦合的设计:UnityEvent允许对象之间…...

食品安全管理员考试真题题库及答案
食品安全管理员考试真题题库及答案 95.对食品生产经营企业来说,实施ISO 22000是()。 A.强制性的 B.无效的 C.自愿的 D.必须的 答案:C 96.CDC的意思是()。 A.卫生监督所 B.疾病控制预防中心 C.卫生…...

【C++】—— vector 的模拟实现
【C】—— vector 的模拟实现 0 前言1 vector 的成员变量1.1 stl 库中的 vector 成员变量1.2 模拟实现 vector 成员变量 2 迭代器3 size、capacity、empty4 opreator[ ]5 reserve5.1 初版 reserve5.2 _finish 的处理5.3 深拷贝5.4 终版 6 push_back 与 pop_back7 打印函数7.1 初…...

MySQL 查询过慢的优化方法
1. 优化查询语句 问题:使用 SELECT * 会导致查询获取不必要的数据。 SELECT * FROM users WHERE age > 30;优化建议: 指定需要的列,这样可以减少数据传输的负担,提升查询速度。 SELECT name, email FROM users WHERE age &g…...

YoloV8修改分类(Classify)的前处理(记录)
修改原因 yolo自带的分类前处理对于长方形的数据不够友好,存在特征丢失等问题修改后虽然解决了这个问题但是局部特征也会丢失因为会下采样程度多于自带的,总之具体哪种好不同数据应该表现不同我的数据中大量长宽比很大的数据所以尝试修改自带的前处理&a…...

半监督学习能否帮助训练更好的模型?
数据科学家面临的最常见挑战之一是缺乏足够的标记数据来训练一个可靠且准确的模型。标记数据对于监督学习任务,如分类或回归至关重要。然而,在许多领域,获取标记数据既昂贵又耗时,有时甚至是不切实际的。另一方面,未标…...

VBA 获取字段标题代码轻松搞定
hi,大家好! 最近又有一段时间没和大家唠嗑了,最近也没有时间给大家开直播,天天忙,但不知道在忙啥!那今天我们来讲点啥好玩的呢? 今天是老师节,那就先祝各位老师节日快乐࿰…...

C++代码片段
for(int i1; i<shuliang; i) { int f100; cout<<a[i].name<<":"<<\n; cout<<"该舰艇现在距离基地"<<km<<"km,需要"<<km…...

Golang | Leetcode Golang题解之第388题文件的最长绝对路径
题目: 题解: func lengthLongestPath(input string) (ans int) {n : len(input)level : make([]int, n1)for i : 0; i < n; {// 检测当前文件的深度depth : 1for ; i < n && input[i] \t; i {depth}// 统计当前文件名的长度length, isFi…...

docker打包前端项目
🎉 前言 之前有出过一期打包后端项目和数据库的教程,现在填个坑,出一期打包前端项目的教程,废话不多说,我们直接进入正题。 🎉 编写Dockerfile文件 老规矩,先描述项目结构,结构图…...

调度器怎么自己写?调度器在实现时需要注意哪些细节?请写一个jvm的调度器?如何在这个调度器中添加多个任务?
如果你想自己编写一个调度器,可以按照以下步骤进行: 一、确定需求和目标 明确调度器的应用场景,例如任务调度、资源分配、进程管理等。 确定调度的对象,比如任务、作业、进程等。 定义调度的目标,如最小化完成时间、最…...

创客匠人对话|德国临床营养学家单场发售百万秘笈大公开
老蒋创客圈第66期对话标杆直播连麦,我们邀请到【梦想身型健康管理学院】平台创始人吴迪老师。为我们分享“健康管理赛道单场发售破百万!创始人背后的操盘秘笈是什么?”,深度剖析如何去展示自己的核心竞争力?如何扩大专…...

开源项目低代码表单FormCreate从Vue2到Vue3升级指南
开源项目低代码表单 FormCreate v3 版本基于 Vue 3.0 构建,尽管功能与 v2 版本大致相同,但有一些重要的变更和不兼容项需要注意。 源码地址: Github | Gitee FormCreate v3 对比 v2 版本在一些功能和配置项上做了调整,以更好地支持 Vue 3 的…...

序偶解释:李冬梅老师书线性表一章第一页
序偶的定义: 有序偶是两个对象的搜集,使得可以区分出其中一个是“第一个元素”而另一个是“第二个元素”。带有第一个元素a和第二个元素b的有序偶通常写为(a,b)。例如,在数学中,有序偶用于表示二维空间上的点。序偶的特性…...

3GPP协议入门——物理层基础(二)
物理层基础(一)在这里~ 物理层基础(一) 1.RE Resource Element,NR中最小的资源单位,时域上是一个OFDM符号长度,频域上为一个子载波宽度。 2. RB Resource Block,时域上是一个OFDM符…...

Java学习Day41:手刃青背龙!(spring框架之事务)
1.spring事务概念 在数据层和业务层保证一系列数据库操作原子性成功失败!(相比事务可以在业务层开启) 1.事务定义:关键字:Transactional(一般写在接口上) 2.事务管理器:在JdbcCon…...

el-image(vue 总)
一 加载静态资源 在第一次使用vue3开发项目时,使用require(‘图片路径’),结果浏览器报错: Uncaught (in promise) ReferenceError: require is not defined 因为require是webpack提供的一种加载能力,但…...