泛型的初步认识(2)

前言~🥳🎉🎉🎉   

hellohello~,大家好💕💕,这里是E绵绵呀✋✋ ,如果觉得这篇文章还不错的话还请点赞❤️❤️收藏💞 💞 关注💥💥,如果发现这篇文章有问题的话,欢迎各位评论留言指正,大家一起加油!一起chin up!👍👍 

💥个人主页:E绵绵的博客
💥所属专栏:JAVA知识点专栏   JAVA题目练习  c语言知识点专栏   c语言题目练习

这篇文章我们将继续介绍泛型,相比于上一篇文章,这篇文章内容更深,更难理解。还请好好阅读消化。

参考文章:Java 中的泛型(两万字超全详解)_java 泛型-CSDN博客 

🎯🎯泛型绝对要注意的一点 

🎯🎯在java中,我们无法直接实例化泛型的类型参数对象.  如存在<T>,我们就不能new T()或者new  T[]等等,凡是牵扯到创建T相关的对象都会报错。

而之所以该行为会报错是因为它牵扯了类型擦除这个很深层的知识点,那么我们来看下类型擦除是什么吧。

类型擦除  

类型擦除的定义 

在Java中,类型擦除是指在编译时期对泛型类型进行擦除,将泛型类型转换为原始类型。(原始类型大部分情况下都是Object类)

❤️❤️换而言之,泛型信息只存在于代码编译阶段,在代码编译结束后,与泛型相关的信息会被擦除掉替换为原始类型,专业术语叫做类型擦除。也就是说,成功编译过后的 class 文件中不包含任何泛型信息,泛型信息不会进入到运行时阶段。这样做的目的是为了保持与旧版本的Java代码的兼容性。

这有一个例子能验证编译时泛型会进行类型擦除,假如我们给 ArrayList 集合传入两种不同的数据类型,并比较它们的类信息:

public class GenericType {public static void main(String[] args) {  ArrayList<String> arrayString = new ArrayList<String>();   ArrayList<Integer> arrayInteger = new ArrayList<Integer>();   System.out.println(arrayString.getClass() == arrayInteger.getClass());// true}  
}

在这个例子中,我们定义了两个 ArrayList 集合,不过一个是 ArrayList< String>,只能存储字符串。一个是 ArrayList< Integer>,只能存储整型对象。我们通过 arrayString 对象和 arrayInteger 对象的 getClass() 方法获取它们的对象信息并比较,发现结果为true。

明明我们在 <> 中传入了两种不同的数据类型,按照上文所说的,它们的类型参数 T 不是应该被替换成我们传入的数据类型了吗,那么结果应该是不同的,那为什么它们的对象信息还是相同呢? 这是因为在编译期间,所有的泛型信息都会被擦除变为原始类型, 所以这两个对象信息在运行时就完全相同,结果就为true。

我们还可以通过观察编译之后生成的的字节码发现一个现象,所有的T编译后都变为Object。

那么是不是所有的类型参数被擦除后都以 Object 类进行替换呢?

 答案是否定的,大部分情况下,类型参数 T 被擦除后都会以 Object 类进行替换;而有一种情况则不是,那就是使用到了 extends 和 super 语法的有界类型参数。

当为上界时,假设定义一个泛型类如下:

public class Caculate<T extends Number> {private T num;
}

  将其反编译:

public class Caculate {public Caculate() {}// 默认构造器,不用管private Number num;
}

可以发现,使用到了 extends (上界)语法的类型参数 T 被擦除后会替换为 Number 而不再是 Object。

public class Example<T super Number> {private T value;public Example(T value) {this.value = value;}public T getValue() {return value;}
}

同理对于下限虽然我们没学,但是我们也要知道在类型擦除上其跟上限差不多,泛型类Example使用super关键字限定了泛型类型参数T的下界为Number,在编译时期,T会被擦除成为Number类型。

类型擦除的原理 

假如我们定义了一个 ArrayList< Integer > 泛型集合,若向该集合中插入 String 类型的对象,不需要运行程序,编译器就会直接报错。这里可能有小伙伴就产生了疑问:

不是说泛型信息在编译的时候就会被擦除掉吗?那既然泛型信息被擦除了,如何保证我们在集合中只添加指定的数据类型的对象呢?换而言之,我们虽然定义了 ArrayList< Integer > 泛型集合,但其泛型信息最终被擦除后就变成了 ArrayList< Object > 集合,那为什么不允许向其中插入 String 对象呢?

Java 是如何解决这个问题的?

  • 其实在创建一个泛型类的对象时, Java 编译器是先检查代码中传入 < T > 的数据类型,并记录下来,然后再对代码进行编译,编译的同时进行类型擦除;如果需要对被擦除了泛型信息的对象进行操作,编译器会自动将对象进行强制类型转换。

我们可以把泛型的类型安全检查机制和类型擦除想象成演唱会的验票机制:以 ArrayList< Integer> 泛型集合为例。

1.当我们在创建一个 ArrayList< Integer > 泛型集合的时候,ArrayList 可以看作是演唱会场馆,而< T >就是场馆的验票系统,Integer 是验票系统设置的门票类型;
2.当验票系统设置好为< Integer >后,只有持有 Integer 门票的人才可以通过验票系统,进入演唱会场馆(集合)中;若是未持有 Integer 门票的人想进场,则验票系统会发出警告(编译器报错)。
3.在通过验票系统时,门票会被收掉(类型擦除),但场馆后台(JVM)会记录下观众信息(泛型信息)。
4.进场后的观众变成了没有门票的普通人(原始数据类型)。但是,在需要查看观众的信息时(操作对象),场馆后台可以找到记录的观众信息(编译器会自动将对象进行类型转换)。

   如下是一个例子:

public class GenericType {public static void main(String[] args) {  ArrayList<Integer> arrayInteger = new ArrayList<Integer>();// 设置验票系统   arrayInteger.add(111);// 观众进场,验票系统验票,门票会被收走(编译时会进行类型擦除)Integer n = arrayInteger.get(0);// 获取观众信息,编译器会自动进行强制类型转换System.out.println(n);}  
}

擦除 ArrayList< Integer > 的泛型信息后,泛型类型参数都变为Object,get() 方法的返回值将返回 Object 类型,但编译器会自动插入 Integer 的强制类型转换。也就是说,编译器把 get() 方法调用翻译为两条字节码指令:

对原始方法 get() 的调用,返回的是 Object 类型;
将返回的 Object 类型强制转换为 Integer 类型;

代码如下:

	Integer n = arrayInteger.get(0);// 这条代码底层如下://(1)get() 方法的返回值返回的是 Object 类型Object object = arrayInteger.get(0);//(2)编译器自动插入 Integer 的强制类型转换Integer n = (Integer) object;

 类型擦除小结

1.泛型信息(包括泛型类、接口、方法)只在代码编译阶段存在,在代码成功编译后,其内的所有泛型信息都会被擦除,并且类型参数 T 会被统一替换为其原始类型(默认是 Object 类,若有 extends 或者 super 则另外分析);

2.在泛型信息被擦除后,若还需要使用到对象相关的泛型信息,编译器底层会自动进行类型转换(从原始类型转换为未擦除前的数据类型)。

 泛型绝对要注意的一点 (续写)

❤️❤️所以我们可以得出原因,在Java中,不能直接使用new关键字创建泛型对象。这是因为Java的泛型是在编译时期进行类型擦除的,即在运行时泛型信息被擦除,只保留原始类型,我们不清楚其原本的具体类型。因此,编译器不允许直接创建泛型对象。

因此如T[] ts = new T[5];是会报错的,那有人这样思考,既然这样的代码不行,那么我们将其修改成这样的代码:T[] array = (T[])new Object[10]; 是否就足够好,答案是未必的。

T[] array = (T[])new Object[10]; 在大部分情况下都是能正常使用的,但是在一些特殊情况下如以下代码是不能正常使用

class MyArray<T> {public T[] array = (T[])new Object[10];
//编译之后类型擦除变为 object[] array=(Object)new Object[10],所以运行时成立//如果编译之后不会进行类型擦除,则会发生类型转换错误public T getPos(int pos) {return this.array[pos];}public void setVal(int pos,T val) {this.array[pos] = val;}public T[] getArray() {return array;}
}public static void main(String[] args) {MyArray<Integer> myArray1 = new MyArray<>();Integer[] strings = myArray1.getArray();//因为array的对象是以Object为实例创建的,所以返回出来也是Object类//如果是返回出Integer,则直接报错,所以编译器此时不会自动强制类型转换//而前面都没报错,我们却在返回出Object时,用Integer接收,所以报错}

所以在这情况下报错了,通俗讲就是:返回的Object数组直接转给Integer类型的数组,编译器认为是不安全的,直接报错。

正确方式应该是但对我们来说,这个又太过复杂了,所以对于这种类似形式的代码:T[] array = (T[])new Object[10];    一般是不采用的。我们大可看一下源码是怎么创建类数组的:

而在我们的源码中类数组的创建都是用 Object[] array = new Object[n];该种形式去创建的,而不是T[] array = (T[])new Object[n]; 

❤️❤️所以以后我们类数组的创建都是直接 Object[] array = new Object[n]; ,切向源码看齐,源码也是这么用的,我们最好不要用T[] array = (T[])new Object[n]; 

总结 

对于这篇文章的内容大家可能看的云里雾里,的确本人也觉得牵涉的很深,很绕。所以其实对于第二部分内容你只要了解清楚类型擦除这个机制不能用new 实例化泛型对象就行了,其他的内容看的懂就看,看不懂也就算了。

所以我们的泛型的初步认识就这样结束啦,对于其泛型的进阶我们会在java数据结构快完结的时候讲。还希望各位大佬们能给个三连,点点关注,点点赞,发发评论呀,感谢各位大佬~❤️❤️💕💕🥳🎉🎉🎉

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

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

相关文章

优思学院|ISO45001职业健康安全管理体系是什么?

ISO45001:2018是新公布的国际标准规范&#xff0c;全球备受期待的职业健康与安全国际标准&#xff08;OH&S&#xff09;于2018年公布&#xff0c;并将在全球范围内改变工作场所实践。ISO45001将取代OHSAS18001&#xff0c;成为全球工作场所健康与安全的参考。 ISO45001:201…

微信域名防封/QQ域名防封/域名状态检测/域名防红防封API平台源码

下载地址&#xff1a;API平台源码 这套源码是使用thinkphp3.1.3开发的&#xff0c;可以在PHP5.3-5.6下运行&#xff0c;程序是有一点老了&#xff0c;但是思路仍在&#xff01;然后&#xff0c;这套源码我已经成功搭建起来了&#xff0c;后台、个人&#xff08;用户&#xff0…

在瑞芯微RV1126 Linux系统上调试WiFi的详细指南

目录标题 1. **系统和环境准备**2. **检查WiFi设备状态**3. **启用和禁用WiFi接口**4. **扫描可用的WiFi网络**5. **连接到WiFi网络**6. **查看当前的WiFi连接状态**7. **断开和重新连接WiFi**8. **管理WiFi网络配置**9. **使用iw工具进行高级WiFi调试**10. **故障排除和日志获…

算法训练营day16

一、二叉树的最大深度 递归解法 后序遍历(DFS) class Solution {public int maxDepth(TreeNode root) {if (root null) return 0;return Math.max(maxDepth(root.left), maxDepth(root.right)) 1;} }算法解析&#xff1a; 终止条件&#xff1a; 当 root 为空&#xff0c;…

力扣---填充每个节点的下一个右侧节点指针 II

给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NULL 。 初始状态下&#xff0c;所有 next 指针都…

牛x之路 - Day1

Day1 微积分之屠龙宝刀&#xff08;武林秘籍&#xff09; 之前的一些东西都在pdf上记得笔记&#xff0c; 没有在这个上面展示一遍&#xff0c;只好学到相关内容的时候再提叙啦&#xff1b;所以其实再写这个小记的时候&#xff0c;我已经看了一半的书&#xff0c;但是不要紧&am…

IntelliJ IDEA运行发布传统Java Web Application项目

接 重温8年前项目部署 要求&#xff0c;如何改用IntelliJ IDEA运行发布传统 Java Web Application项目呢&#xff0c;简述步骤如下&#xff1a; 一、下载源码 源码&#xff1a;https://github.com/wysheng/kindergarten 下载后的本地项目路径&#xff1a;/Users/songjianyon…

《Python源码剖析》之对象的基石---PyObject

前言 在python的源代码中&#xff0c;PyObject的结构体定义如下&#xff0c;它的内容看起来很简单&#xff0c;只有3项&#xff0c;分别是&#xff1a;_PyObject_HEAD_EXTRA&#xff0c;ob_refcnt和ob_type&#xff0c;其中_PyObject_HEAD_EXTRA是用于指向活动堆的指针&#x…

学习大数据,所需要的linux基础(1)

文章目录 linux入门概述Linux和Windows的区别CentOS下载地址 Linux文件与目录结构Linux文件Linux目录结构 VI/VIM编辑器vi/vim是什么测试数据集准备一般模式编辑模式指令模式模式间转换 网络配置和系统管理操作查看网络IP和网关配置网络和ip地址ifconfig配置网络接口修改ip地址…

使用Python进行自动化测试

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 如何使用Python进行自动化测试&#xff1a;测试框架的选择与应用 自动化测试是软件开发过程…

curlftpfs和fusermount

curlftpfs 是一种 Linux 系统下用来将 FTP 服务器挂载为文件系统的工具&#xff0c;这意味着可以通过本地目录来访问和操作 FTP 服务器上的文件。 挂载FTP服务器到本地系统 为了挂载FTP服务器到本地系统中&#xff0c;使用curlftpfs工具&#xff0c;可以按照以下格式书写命令…

保姆级教程!QRCNN-BiLSTM一键实现多变量回归区间预测!区间预测全家桶再更新!

​ 声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 今天对我们之前推出的区间预测全家桶进行…

进程间通信IPC(二)

一、存储映射I/O(Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是从缓冲区中取数据&#xff0c;就相当于读文件中的相应字节。与此类似&#xff0c;将数据存入缓冲区&#xff0c;则相应的字节就自动写入文件。这样&#xff0c;就可在不使用read和write…

由于找不到msvcp110d.dll,无法继续执行代码

在计算机软件开发和运行环境中&#xff0c;动态链接库&#xff08;DLL&#xff09;文件扮演着至关重要的角色。它们封装了特定功能的代码&#xff0c;使得多个应用程序能够共享这些功能而无需重复编译或加载相同的代码&#xff0c;从而显著提升了系统资源利用率和软件开发效率。…

024——驱动、server、client、GUI全功能联调

目录 一、本次修改 二、GUI和Client之间联调 2.1 工程结构修改 2.2 将TCP程序修改为可被其它程序调用 2.3 优化显示界面 2.4 解决GUI通过tcp send的问题 2.5 处理服务器数据 时间不是很多了&#xff0c;我想压缩一下快点把属于毕设的这部分搞完&#xff0c;俺要出去旅游…

【HTML】H5新增元素记录

H5 新增元素特性 1. 语义化标签 语义化标签的好处&#xff1a; 对于浏览器来说&#xff0c;标签不够语义化对于搜索引擎来说&#xff0c;不利于SEO的优化 语义化标签&#xff1a; header:头部元素nav&#xff1a;导航section:定义文档某个区域的元素article:内容元素aside…

解锁多智能体路径规划新境界:结合启发式搜索提升ML本地策略

引言&#xff1a;多智能体路径寻找&#xff08;MAPF&#xff09;问题的重要性与挑战 在现代自动化和机器人技术迅速发展的背景下&#xff0c;多智能体路径寻找&#xff08;Multi-agent path finding&#xff0c;简称MAPF&#xff09;问题的研究变得日益重要。MAPF问题涉及为一…

【NTN 卫星通信】NTN的SSB波束探讨

1 概述 SSB是同步广播信道&#xff0c;用于小区搜索&#xff0c;主系统消息的发送。NR协议中定义了多种SSB波束格式&#xff0c;简述如下。   小区搜索是终端获取与小区的时间和频率同步并检测小区的物理层小区ID的过程。   为了进行小区搜索&#xff0c;UE接收以下同步信号…

MySQL Workbench下载安装、 MySQL Workbench使用

官方下载链接;MySQL :: Download MySQL Workbench 下载好懒人安装&#xff0c;也可自己选择目录 下面是使用&#xff1a; 连接数据库&#xff1a; 填写数据库连接信息&#xff1a; 基本操作部分&#xff1a; 数据导入导出&#xff1a; 导出/备份 导入&#xff1a; 生产er图…

【热门话题】探索与心得:深入体验Microsoft Edge浏览器

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 探索与心得&#xff1a;深入体验Microsoft Edge浏览器一、Edge浏览器概述1.1 发…