数据结构 | 泛型 | 擦除机制| 泛型的上界

目录
编辑
1.泛型
1.1Object类引出泛型概念
2.泛型语法
2.1泛型编写代码
3.泛型的机制
3.1擦除机制
4.泛型的上界
4.1泛型上界的语法
4.2泛型上界的使用
5.泛型方法
5.1泛型方法语法
5.2泛型方法的使用
1.泛型
一般的类和方法中,只能使用具体的代码来实现同一种类型数据的操作。比如一个数组里面存储的是同一种类型,这种存储方式太过于死板。因此JDK1.5引入了新的语法:泛型,通俗的来讲泛型就是多种数据类型(泛滥),从代码上来说就是实现了不同类型之间的存储,因此当我们想要存储各种各样的数据时,我们会使用到泛型。
1.1Object类引出泛型概念
在泛型之前,我们在Object类中学到了,所有类的父类都是Object类,因此我们能把一个数组设置为Object类型呢,这样就能达到数组里面存放各种各样的元素。所以我们可以这样去写代码:
class MyArray {//Object类型数组Object[] arr = new Object[3];public void show() {//分别初始化三种不同类型的数据arr[0] = 1;arr[1] = 1.2;arr[2] = "abc";//遍历arr数组for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}
}
public class Test {public static void main(String[] args) {MyArray myArray = new MyArray();myArray.show();}
}
运行后输出:

以上代码是可以很好的运行,因为我们直接初始化了Object类中的元素。当我们使用get、set方法来实现时就会发现不同处。
class MyArray {//Object类型数组public Object[] arr = new Object[3];//提供get方法public Object getPos(int pos) {return this.arr[pos];}//提供set方法public void setArr(int pos,Object value) {this.arr[pos] = value;}
}public class Test {public static void main(String[] args) {MyArray myArray = new MyArray();//分别设置了三种不同类型的元素myArray.setArr(0,1);myArray.setArr(1,1.2);myArray.setArr(2,"abc");//分别输出了三种不同类型的元素System.out.println(myArray.getPos(0));System.out.println(myArray.getPos(1));//输出字符类型时,报错System.out.println(myArray.getPos(3));}
}
运行后输出:
我们发现,当我往数组中添加了一个字符串时就会出现异常。所以,Object在存储不同类型的时候
还是会出现错误。因此,我们可以想到既然不让我存不同类型的数据,那么我就存同一种类型的数据就好了,这时我们就可以用到泛型。它可以将不同类型数据存储在不同的对象中,但在不同的对象中每一个元素的类型是相同的。
2.泛型语法
(1)语法1
//泛型类语法格式
class 泛型类名称<类型形参列表> {//内容
}
//泛型类中形参可为多个
class ClassName<T,S,B,U> {//内容
}
上述代码中,我们可以看到泛型类与普通类多了一个<>其余并无太大差异,注意<>内可写多个参数。
(2)语法2
//泛型类继承类或泛型类
class 泛型类名称<类型形参列表> extends 类名 {}
//泛型类继承泛型类
class ClassName1<T,S,B,U> extends ClassName2<T> {}
上述代码,表示了泛型类可以继承一个普通类,也可以继承一个泛型类。
2.1泛型编写代码
因此,我们可以这样去编写一段泛型代码:
class MyArray<T> {//创建一个泛型数组T[] arr= (T[])new Object[3];//get方法public T getPos (int pos) {return this.arr[pos];}//set方法public void setArr (int pos,T value) {this.arr[pos] = value;}
}
public class Test {public static void main(String[] args) {//myArray1对象设置int类型数据MyArray<Integer> myArray1 = new MyArray<>();myArray1.setArr(0,1);myArray1.setArr(1,2);myArray1.setArr(2,3);//myArray2对象设置String类型数据MyArray<String> myArray2 = new MyArray<>();myArray2.setArr(0,"a");myArray2.setArr(1,"b");myArray2.setArr(2,"c");//通过get方法输出各个下标元素System.out.print(myArray1.getPos(0)+" ");System.out.print(myArray1.getPos(1)+" ");System.out.print(myArray1.getPos(2)+" ");System.out.print(myArray2.getPos(0)+" ");System.out.print(myArray2.getPos(1)+" ");System.out.print(myArray2.getPos(2));}
}
运行后输出:

以上代码,就是泛型的一个体现,我们要想设置什么类型的数据就在<>里面设置什么类型的包装类即可。上述代码中相信get和set方法对于大家来说不是很难理解,但很多小伙伴第一件见这种代码,可能有些问题不太清楚,因此我来做出解释:
- 类名后面的<T>代表着占位符,表示着当前类为一个泛型类。<>里面的内容可以任意填写,你可以输入E、K、N等等。注意应当填写见名思意内容如T代表着type,N代表着number。
- T[] arr = new T[3];是不可行的,因为泛型不能直接new一个数组,但是我们可以强制类型转换如T[] arr = (T[]) new Object[];。
- 实例化泛型类时应当在<>只能是引用类型不得是基本类型因此通常我们填写包装类,并且该对象中值的类型要一致。
- 实例化泛型类对象时,前面<>内内容不得省略,后面<>内容可以省略。如:Array<String> array = new Array<> ();。
3.泛型的机制
泛型是一种运行时的机制,它会在编译时给我们指出一些错误,也会在获取元素时帮助我们进行强制类型转换。
(1)编译时指出错误
public static void main(String[] args) {MyArray<int> myArray = new MyArray<>();}
报错:

上述报错,代表着泛型类中<>内容类型不能被定义为int。在上文中我们知道了,泛型类<>里面得是一个引用类型,因此int不能作为<>内参数。再比如以下代码:
public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<>();myArray.setArr(0,"abc");}
报错:
上述代码,我们在给set方法传参的时候传了一个String类型的数据,并不符合myArray这个对象的属性。因此造成报错现象。
(2)帮助进行强制类型转换
class MyArray<T> {//创建一个泛型数组public Object[] arr = new Object[3];//get方法public T getPos (int pos) {return (T)arr[pos];}//set方法public void setArr (int pos,T value) {arr[pos] = value;}
}
public class Test {public static void main(String[] args) {//Integer泛型类MyArray<Integer> myArray = new MyArray<>();//自动帮我们进行类型转换了myArray.setArr(0,23);//String泛型类MyArray<String> myArray1 = new MyArray<>();//自动帮我们进行类型转换了myArray1.setArr(0,"abc");}
}
以上代码中,set方法形参列表第二个参数value为泛型T类型,但是在main方法中。我在给setArr方法传参的时候,直接传了一个整型和一个字符串。编译器并没有报错,那是因为泛型自动帮助我们进行了强制类型转换,也就是把T类型分别转成了整型和字符串型。
3.1擦除机制
通过上述讲解我们知道了,泛型会在我们编译时显示错误会帮助我们强制类型转换。表明了泛型是一种编译时的机制。那我们的泛型在运行后会是什么样呢?其实我们的泛型在编译后会被擦除为Object类型。
class MyArray<T> {//创建一个泛型数组T[] arr= (T[])new Object[3];//get方法public T getPos (int pos) {return this.arr[pos];}//set方法public void setArr (int pos,T value) {this.arr[pos] = value;}
}
public class Test {public static void main(String[] args) {//实例一个泛型为Integer类的对象MyArray<Integer> myArray = new MyArray<>();myArray.setArr(0,1);myArray.setArr(1,2);//实例一个泛型为String类的对象MyArray<String> myArray1 = new MyArray<>();myArray1.setArr(0,"abc");myArray1.setArr(1,"def");}
}
调试后:

我们可以发现明明我们在创建数组的时候是这样的 T[] arr= (T[])new Object[3];,但编译器后台自动给我们编程了Object类型。因此,我们可以知道运行后编译器会擦除泛型类型给我们转换为Object类型。
所以,我们可以这样创建一个泛型数组:
class MyArray<T> {//创建一个泛型数组public Object[] arr = new Object[3];//get方法public T getPos (int pos) {return (T)arr[pos];}//set方法public void setArr (int pos,T value) {arr[pos] = value;}
}

上述代码对数组进行一个初始化才是地道的初始化,而原来的T[] arr = (T[]) new T[3];并不是很地道,但也能达到效果。
4.泛型的上界
有一程序要求通过泛型找出一个数组的最大值,因此有以下代码:
class MaxArray<T> {public void findMax(T[] arr) {T max = arr[0];for (int i = 0; i < arr.length; i++) {if (max > arr[i]) {max = arr[i];}}System.out.println(max);}
}
public class Test {public static void main(String[] args) {MaxArray<Integer> maxArray =new MaxArray<>();Integer[] integers = {1,3,4,5,10,8,9,5,20};maxArray.findMax(integers);}
}
运行后报错:

上述代码报错原因是if里面的判断,当基本类型之间进行判断时可以使用算术符,当基本类型与引用类型之间进行判断时我们就得使用Comparable方法来判断。但是我们发现上述泛型并没有使用Comparable接口,因此我们可以使泛型继承这个接口就可以实现该操作,那么这样一个操作就代表着泛型上界这样一个概念。
4.1泛型上界的语法
class 泛型类<参数列表 extems 类型边界> {//内容}
以上代码就是泛型类中参数类型继承一个类型边界的创建,实例:
class Array<T extends Number> {//内容
}
只接受 Number 的子类型作为 T 的类型实参,因此只有关于Number的子类型我们能使用 ,比如Integer。String类型就不能。如:
class Num<T extends Number> {//以下三个都行Num<Integer> num1 = new Num<>();Num<Byte> num2 = new Num<>();Num<Double> num3 = new Num<>();//会报错Num<String> num4 = new Num<>();
}
报错:

jdk-8帮助手册中描述了以下类为Number子类。

4.2泛型上界的使用
还是4.1中那段代码,我们既然不能使用<来比较一个基本类型和一个引用类型,那我们就使T类型继承Comparable接口。
class MaxArray<T extends Comparable<T>> {public void findMax(T[] arr) {T max = arr[0];for (int i = 0; i < arr.length; i++) {//使用了Comparable接口中的compareTo方法if (max.compareTo(arr[i]) < 0) {max = arr[i];}}System.out.println(max);}
}
public class Test {public static void main(String[] args) {MaxArray<Integer> maxArray =new MaxArray<>();Integer[] integers = {1,3,4,5,10,8,9,5,20};maxArray.findMax(integers);}
}
运行后输出:

以上代码中泛型类中T类型继承了Comparable接口,因此if判断里面可以使用Comparable接口中的compareTo方法。此方法返回的值小于0代表前者比后者小,返回值等于0代表前者与后者相等,返回值大于0代表前者比后者大。
5.泛型方法
在上面我们学习到了泛型类的使用,那么泛型也是有方法的。我们可以把一个普通方法变成泛型方法去使用,那么泛型方法具体有什么用呢?下面我就来讲解:
5.1泛型方法语法
泛型方法的语法格式为:方法限定修饰符<类型形参列表> 返回值类型 方法名称(参数列表){ //内容 }。
实例:
public <T> T maxNumber(T[] arr) {//内容}
5.2泛型方法的使用
求数组中的最大数:
class Array {public <T extends Comparable<T>> T maxNum(T[] num){T max = num[0];for (int i = 0; i < num.length; i++) {if (max.compareTo(num[i]) < 0) {max = num[i];}}return max;}
}
public class Test {public static void main(String[] args) {Array array = new Array();Integer[] arr = {1,23,4,5,6,7};Integer max=array.<Integer>maxNum(arr);System.out.println(max);}
}
运行后输出:

以上代码展示了泛型方法的使用,我们可以看到泛型方法的语法比较抽象,这就是泛型方法的难点之处。
本期博客到这里就结束了,感谢各位的阅读。

下期预告:ArrayList与顺序表
相关文章:
数据结构 | 泛型 | 擦除机制| 泛型的上界
目录 编辑 1.泛型 1.1Object类引出泛型概念 2.泛型语法 2.1泛型编写代码 3.泛型的机制 3.1擦除机制 4.泛型的上界 4.1泛型上界的语法 4.2泛型上界的使用 5.泛型方法 5.1泛型方法语法 5.2泛型方法的使用 1.泛型 一般的类和方法中,只能使用具体的代码…...
C++拷贝构造函数(复制构造函数)详解
拷贝和复制是一个意思,对应的英文单词都是copy。对于计算机来说,拷贝是指用一份原有的、已经存在的数据创建出一份新的数据,最终的结果是多了一份相同的数据。例如,将 Word 文档拷贝到U盘去复印店打印,将 D 盘的图片拷…...
python学习——多线程
python学习——多线程概念python中线程的开发线程的启动线程的退出和传参threading的属性和方法threading实例的属性和方法多线程daemon线程和non-demone线程daemon线程的应用场景线程的jointhreading.local类线程的延迟执行:Timer线程同步Event 事件Lock ——锁加锁…...
SAP 系统中过账码or记账码
SAP中过账码和记账码是指同一个事物。 在实际业务中,记账码就是只有“借”和“贷”, 而SAP中Posting Code肩负着更多的任务: 1)界定科目类型, 2)借贷方向, 3)凭证输入时画面上的字…...
【FreeRTOS(一)】FreeRTOS新手入门——初识FreeRTOS
初识FreeRTOS一、实时操作系统概述1、概念2、RTOS的必要性3、RTOS与裸机的区别4、FreeRTOS的特点二、FreeRTOS的架构三、FreeRTOS的代码架构一、实时操作系统概述 1、概念 RTOS:根据各个任务的要求,进行资源(包括存储器、外设等)…...
Python中 __init__的通俗解释是什么?
__init__是Python中的一个特殊方法,用于在创建对象时初始化对象的属性。通俗来讲,它就像是一个构造函数,当我们创建一个类的实例时,__init__方法会被自动调用,用于初始化对象的属性。 举个例子,如果我们定义…...
网友真实面试总结出的自动化测试面试题库
目录 常规问题 手工测试部 自动化测试 自动化测试面试题2:selenium篇 常规问题 1、如何快速深入的了解移动互联网领域的应用 (答案:看http协议 restful api知识 json加1分) 2、对xx应用自己会花多久可以在业务上从入门到精通&…...
2023 年最佳 C++ IDE
文章目录前言1. Visual Studio2. Code::Blocks3. CLion4. Eclipse CDT(C/C 开发工具)5. CodeLite6. Apache NetBeans7. Qt Creator8. Dev C9. C Builder10. Xcode11. GNAT Programming Studio12. Kite总结前言 要跟踪极佳 IDE(集成开发环境&…...
在Ubuntu上使用VSCode编译MySQL Connector/C连接库
首先下载并解压MySQL Connector/C源码,然后执行以下步骤: 1、安装MySQL Connector/C依赖:在终端中输入以下命令来安装MySQL Connector/C的依赖项: sudo apt-get install build-essential cmake 2、下载并解压MySQL Connector/C源…...
单声道数字音频放大器AD87589
AD87589是一种集成音频系统解决方案,嵌入数字音频处理、功率级放大器和立体声2Vrms线路驱动器。 AD87589具有可编程转换速率控制的输出缓冲器,可直接驱动一个(单声道)或两个(立体声)扬声器。此外࿰…...
网络的UDP协议和TCP协议
协议:数据在网络中的传输规则,常见的协议有 UDP协议和TCP协议 协议:计算机网络中,连接和通信的规则被称为网络通信协议 UDP协议:用户数据报协议,是面向无连接通信协议,速度快,有大小…...
【JaveEE】多线程之阻塞队列(BlockingQueue)
目录 1.了解阻塞队列 2.生产者消费者模型又是什么? 2.1生产者消费者模型的优点 2.1.1降低服务器与服务器之间耦合度 2.1.2“削峰填谷”平衡消费者和生产的处理能力 3.标准库中的阻塞队列(BlockingQueue) 3.1基于标准库(Bloc…...
分布式ELK日志监控系统环境搭建
文章目录1.1为什么需要监控项目日志1.2ELK日志监控系统介绍1.3ELK的工作流程1.4ELK环境搭建1.4.1Elasticsearch的安装1.4.2Kibana的安装1.4.3Logstash的安装1.4.4数据源配置1.4.5日志监测测试1.4.6日志数据可视化展示1.1为什么需要监控项目日志 项目日志是记录项目运行过程中产…...
【数据结构刷题集】链表经典习题
😽PREFACE🎁欢迎各位→点赞👍 收藏⭐ 评论📝📢系列专栏:数据结构刷题集🔊本专栏涉及到题目是数据结构专栏的补充与应用,只更新相关题目,旨在帮助提高代码熟练度&#x…...
JavaSE(3.27) 异常
学习不要眼高手低,学习是一点点积累的。即使你现在很菜,坚持学一个学期不会差的!只要花时间学习,每天都是进步的,这些进步可能你现在看不到,但是不要小瞧了积累效应,30天,60天&#…...
【看门狗】我说的是定时器不是狗啊
单片机在运行中死机了,你或许只能按2下电源键(重启)或1下复位键。 这里简单说一下重启和复位: 从RESET引脚复位,只有MCU复位。而外设看情况,有的可能会有MCU同步复位或者重新初始化。也有可能一些保持复位…...
24万字智慧城市顶层设计及智慧应用解决方案
本资料来源公开网络,仅供个人学习,请勿商用,如有侵权请联系删除。部分资料内容: 4.8 机房消防系统 4.8.1消防系统概况 根据本工程机房消防系统的特殊要求,整个消防系统由火灾报警系统、消防联动系统和气体灭火系统三部…...
跨境电商卖家工具——跨境卫士内容介绍
一、简介 跨境卫士是一款集合多种跨境电商工具的综合软件,由知名跨境电商服务商跨境通开发。跨境卫士可以帮助卖家完成海外物流管理、订单处理、报关报税、市场营销等多项任务,同时还提供数据分析、客户服务、运营管理等一系列支持功能,方便卖…...
Redis 常用基本命令
关于 redis 的常用基本命令 目录 关于 redis 的常用基本命令 1. 关于 key 的操作 2. HyperLogLog 求近似基数 3. 排序相关命令 4. Limit 限制查询 1. 关于 key 的操作 判断某个 key 是否存在 # 格式: exists key exists name# 存在name 返回1 # 不存在name 返回0 查找或…...
【Leetcode】队列的性质与应用
文章目录225. 用队列实现栈示例:提示:分析:题解:622. 设计循环队列示例:提示:分析:题解:225. 用队列实现栈 请你仅使用两个队列实现一个后入先出(LIFO)的栈&…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...

