【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 种形式:

  1. <?> :被称作无限定的通配符。
  2. <? extends T> :被称作有上界的通配符。
  3. <? 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.总结 

所以我们泛型的进阶部分就结束了,把通配符讲完了,我们数据结构部分也就结束了。接下来将学习新的篇章——数据库,数据库会不会开一个新的专栏有待商酌。

在此,我们诚挚地邀请各位大佬们为我们点赞、关注,并在评论区留下您宝贵的意见与建议。让我们共同学习,共同进步,为知识的海洋增添更多宝贵的财富!🎉🎉🎉❤️❤️💕💕🥳👏👏👏  

 

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

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

相关文章

MT6895(天玑8100)处理器规格参数_MTK联发科平台方案

MT6895平台 采用台积电5nm工艺&#xff0c;与天玑 8000 相比性能提升 20% &#xff0c;搭载4 个 2.85GHz A78 核心 4 个 2.0GHz A55 核心&#xff0c;CPU能效比上一代提高 25% 。GPU 采用了第三代的Valhall Arm Mali-G610 MC6架构&#xff0c;拥有6核心&#xff0c;搭配天玑81…

逻辑函数的公式化简法

目录 逻辑函数的公式化简法 并项法 吸收法 消去法 配项法 习题1 习题2 逻辑函数的公式化简法 并项法 B或B非结果为1&#xff0c;这样可以消去B&#xff0c;将两项合并为一项。 用于消去变量。 吸收法 1或上一个变量结果为1&#xff0c;1与上A变量结果为A变量。 1或任何…

将BAT脚本设置为Windows开机自启动

1. 打开“运行”对话框 方法&#xff1a;按下键盘上的 Win R 快捷键&#xff0c;这会立即打开“运行”对话框。输入命令&#xff1a;在“运行”对话框中&#xff0c;输入 shell:startup 并按回车键。 2. 访问“启动”文件夹 目的&#xff1a;shell:startup 命令将直接打开当…

2. Python之注释及缩进以及print和input函数使用

一. Python代码中的注释及缩进 Python中注释有单行注释&#xff0c;多行注释&#xff0c;声明注释 单行注释使用符号#&#xff0c;从#开始&#xff0c;后面到本行末尾都是注释内容。多行注释可以使用’‘’ ‘’三个引号&#xff0c;三个引号内的内容都是注释声明注释&#x…

解决:Module build failed (from ./node_modules/sass-loader/dist/cjs.js)问题

一、问题 Module build failed (from ./node_modules/sass-loader/dist/cjs.js): Error: Cannot find module sass 二、解决方法 1.清除缓存 npm cache clean --force2.重构项目 npm install 3.更新&#xff08;获取最新的&#xff09;node-sass和sass-loader依赖包 npm …

MySQL数据库启用安全审计功能5.7版本

MySQL数据库启用安全审计功能5.7版本&#xff0c;需要使用插件server_audit.dllmysql安装是没有自带这个插件的需要去下载&#xff0c;也可以使用我上传的 操作步骤&#xff1a; 1.把插件复制放到自己安装的mysql下的插件位置&#xff1a;比如我的 C:\zz\ProgramFiles\MySQL\…

HarmonyOs 应用基础--ArkTS-核心-基础

目录 八. ArkTS-语句-类型进阶与渲染控制 1. 对象进阶 1.1. 定义对象数组 1.2. 使用对象数组 2. 渲染控制 - ForEach 2.1. ForEach语法 2.2. ForEach使用优化代码 2.3. 案例-学生档案 实现思路 3. Math对象 4. 综合案例 -- 抽奖卡案例 4.1. 初始页面布局&#xff08;静…

通信工程学习:什么是GSMP通用交换机管理协议

GSMP&#xff1a;通用交换机管理协议 GSMP&#xff08;General Switch Management Protocol&#xff0c;通用交换机管理协议&#xff09;是一种用于IP交换机对ATM交换机进行控制的协议。以下是对GSMP的详细解释&#xff1a; 一、定义与概述 GSMP是一种异步协议&#xff0c;它在…

在国产芯片上实现YOLOv5/v8图像AI识别-【4.4】RK3588网络摄像头推理后推流到RTSP更多内容见视频

本专栏主要是提供一种国产化图像识别的解决方案&#xff0c;专栏中实现了YOLOv5/v8在国产化芯片上的使用部署&#xff0c;并可以实现网页端实时查看。根据自己的具体需求可以直接产品化部署使用。 B站配套视频&#xff1a;https://www.bilibili.com/video/BV1or421T74f 前言…

【论文阅读】01-Survey on Temporal Knowledge Graph

原文名称&#xff1a;Survey on Temporal Knowledge Graph 1 Introduction 目前有两种方法:基于距离模型的嵌入变换方法和基于语义匹配模型的双线性模型。它们的思想都是将包含实体和关系的知识图谱嵌入到连续的低纬度实向量空间中 时间知识图的推理有两种&#xff0c;第一种是…

斐纳切数列考试题

计算机二级考试有一道题 result [] a,b0,1 while a<100:print(a,end,) a, b b, ab # 0,1,1,2,3,5,8,13,21,34,55,89,

python爬虫代理ip池搭建

最近大量爬取数据的时候总会遇到被封ip的情况&#xff0c;所有打算自己搭建一个代理ip池来使用。本次使用的是开源的ip代理池项目ProxyPool 1.下载redis数据库 redis安装 这里我选择直接下载redis的解压包形式&#xff0c;方便安装。下载地址&#xff1a;发布 TPORADOWSKI/RED…

SCAU算法竞赛入门指北

首先&#xff0c;还是欢迎各位小朋友来到华南农业大学&#xff0c;虽然不是什么ACM强校&#xff0c;但是姑且还是有这么个校队存在的。本文的主要目的是给各位OI✌️介绍下acm和oi的区别&#xff0c;给各位纯萌新介绍下你需要做什么&#xff0c;以及进校队的时间线。 ACM是什么…

UE4_后期处理_后期处理材质及后期处理体积二

效果&#xff1a; 步骤&#xff1a; 1、创建后期处理材质,并设置参数。 2、回到主界面&#xff0c;找到需要发光的物体的细节面板。 渲染自定义深度通道&#xff0c;默认自定义深度模具值为10&#xff08;需要修改此值&#xff0c;此值影响物体的亮度&#xff09;。 3、添加…

JVM系列(六) -对象的创建过程

一、摘要 在之前的文章中,我们介绍了类加载的过程和 JVM 内存布局相关的知识。本篇我们综合之前的知识,结合代码一起推演一下对象的真实创建过程,以及对象创建完成之后在 JVM 中是如何保存的。 二、对象的创建 在 Java 中,创建对象的方式有很多种,比如最常见的通过new …

Spring 循环依赖原理及解决方案

一、什么是循环依赖 循环依赖指的是一个实例或多个实例存在相互依赖的关系&#xff08;类之间循环嵌套引用&#xff09;。 举例&#xff1a; Component public class AService {// A中注入了BAutowiredprivate BService bService; }Component public class BService {// B中也…

ActiveMQ 的网络连接及消息回流机制

1、ActiveMQ 的网络连接 activeMQ 如果要实现扩展性和高可用性的要求的话&#xff0c;就需要用用到网络连接模式。 NetworkConnector&#xff1a;主要用来配置 broker 与 broker 之间的通信连接 如上图所示&#xff0c;MQ 服务器1 和MQ 服务器2 通过 NewworkConnector 相连&…

前端月中总结

1、领导一拍脑门想要一个内部聊天软件 --基于open IM二次开发 背景 前段时间不是接手了一个内部办公软件的项目嘛&#xff0c;这个项目已经写了三四年了&#xff0c;一代代的前端融合了不知到多少种代码风格&#xff0c;再加上最初搭这个项目架子的人不知道咋想的&#xff0c…

操作系统 ---- 进程的概念、组成、特征

学习路线&#xff1a; 一、进程的概念及组成 我们通过一个例子来说明进程的概念以及程序和进程的区别。 我们在Windows操作系统中打开任务管理器&#xff0c;在任务管理器当中能看到此时系统当中运行的进程有哪些&#xff0c;如下图所示&#xff1a; 此时&#…

H5漂流瓶社交系统源码

一个非常有创意的H5漂流瓶社交系统源码&#xff0c;带完整前端h5和后台管理系统。 环境&#xff1a;Nginx 1.20.1-MySQL 5.6.50-PHP-7.3 代码下载