当前位置: 首页 > news >正文

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

目录

​编辑

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方法对于大家来说不是很难理解,但很多小伙伴第一件见这种代码,可能有些问题不太清楚,因此我来做出解释:

  1. 类名后面的<T>代表着占位符,表示着当前类为一个泛型类。<>里面的内容可以任意填写,你可以输入E、K、N等等。注意应当填写见名思意内容如T代表着type,N代表着number。
  2. T[] arr = new T[3];是不可行的,因为泛型不能直接new一个数组,但是我们可以强制类型转换如T[] arr = (T[]) new Object[];。
  3. 实例化泛型类时应当在<>只能是引用类型不得是基本类型因此通常我们填写包装类,并且该对象中值的类型要一致。
  4. 实例化泛型类对象时,前面<>内内容不得省略,后面<>内容可以省略。如: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.泛型 一般的类和方法中&#xff0c;只能使用具体的代码…...

C++拷贝构造函数(复制构造函数)详解

拷贝和复制是一个意思&#xff0c;对应的英文单词都是copy。对于计算机来说&#xff0c;拷贝是指用一份原有的、已经存在的数据创建出一份新的数据&#xff0c;最终的结果是多了一份相同的数据。例如&#xff0c;将 Word 文档拷贝到U盘去复印店打印&#xff0c;将 D 盘的图片拷…...

python学习——多线程

python学习——多线程概念python中线程的开发线程的启动线程的退出和传参threading的属性和方法threading实例的属性和方法多线程daemon线程和non-demone线程daemon线程的应用场景线程的jointhreading.local类线程的延迟执行&#xff1a;Timer线程同步Event 事件Lock ——锁加锁…...

SAP 系统中过账码or记账码

SAP中过账码和记账码是指同一个事物。 在实际业务中&#xff0c;记账码就是只有“借”和“贷”&#xff0c; 而SAP中Posting Code肩负着更多的任务&#xff1a; 1&#xff09;界定科目类型&#xff0c; 2&#xff09;借贷方向&#xff0c; 3&#xff09;凭证输入时画面上的字…...

【FreeRTOS(一)】FreeRTOS新手入门——初识FreeRTOS

初识FreeRTOS一、实时操作系统概述1、概念2、RTOS的必要性3、RTOS与裸机的区别4、FreeRTOS的特点二、FreeRTOS的架构三、FreeRTOS的代码架构一、实时操作系统概述 1、概念 RTOS&#xff1a;根据各个任务的要求&#xff0c;进行资源&#xff08;包括存储器、外设等&#xff09…...

Python中 __init__的通俗解释是什么?

__init__是Python中的一个特殊方法&#xff0c;用于在创建对象时初始化对象的属性。通俗来讲&#xff0c;它就像是一个构造函数&#xff0c;当我们创建一个类的实例时&#xff0c;__init__方法会被自动调用&#xff0c;用于初始化对象的属性。 举个例子&#xff0c;如果我们定义…...

网友真实面试总结出的自动化测试面试题库

目录 常规问题 手工测试部 自动化测试 自动化测试面试题2&#xff1a;selenium篇 常规问题 1、如何快速深入的了解移动互联网领域的应用 &#xff08;答案&#xff1a;看http协议 restful api知识 json加1分&#xff09; 2、对xx应用自己会花多久可以在业务上从入门到精通&…...

2023 年最佳 C++ IDE

文章目录前言1. Visual Studio2. Code::Blocks3. CLion4. Eclipse CDT&#xff08;C/C 开发工具&#xff09;5. CodeLite6. Apache NetBeans7. Qt Creator8. Dev C9. C Builder10. Xcode11. GNAT Programming Studio12. Kite总结前言 要跟踪极佳 IDE&#xff08;集成开发环境&…...

在Ubuntu上使用VSCode编译MySQL Connector/C连接库

首先下载并解压MySQL Connector/C源码&#xff0c;然后执行以下步骤&#xff1a; 1、安装MySQL Connector/C依赖&#xff1a;在终端中输入以下命令来安装MySQL Connector/C的依赖项&#xff1a; sudo apt-get install build-essential cmake 2、下载并解压MySQL Connector/C源…...

单声道数字音频放大器AD87589

AD87589是一种集成音频系统解决方案&#xff0c;嵌入数字音频处理、功率级放大器和立体声2Vrms线路驱动器。 AD87589具有可编程转换速率控制的输出缓冲器&#xff0c;可直接驱动一个&#xff08;单声道&#xff09;或两个&#xff08;立体声&#xff09;扬声器。此外&#xff0…...

网络的UDP协议和TCP协议

协议&#xff1a;数据在网络中的传输规则&#xff0c;常见的协议有 UDP协议和TCP协议 协议&#xff1a;计算机网络中&#xff0c;连接和通信的规则被称为网络通信协议 UDP协议&#xff1a;用户数据报协议&#xff0c;是面向无连接通信协议&#xff0c;速度快&#xff0c;有大小…...

【JaveEE】多线程之阻塞队列(BlockingQueue)

目录 1.了解阻塞队列 2.生产者消费者模型又是什么&#xff1f; 2.1生产者消费者模型的优点 2.1.1降低服务器与服务器之间耦合度 2.1.2“削峰填谷”平衡消费者和生产的处理能力 3.标准库中的阻塞队列&#xff08;BlockingQueue&#xff09; 3.1基于标准库&#xff08;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为什么需要监控项目日志 项目日志是记录项目运行过程中产…...

【数据结构刷题集】链表经典习题

&#x1f63d;PREFACE&#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐ 评论&#x1f4dd;&#x1f4e2;系列专栏&#xff1a;数据结构刷题集&#x1f50a;本专栏涉及到题目是数据结构专栏的补充与应用&#xff0c;只更新相关题目&#xff0c;旨在帮助提高代码熟练度&#x…...

JavaSE(3.27) 异常

学习不要眼高手低&#xff0c;学习是一点点积累的。即使你现在很菜&#xff0c;坚持学一个学期不会差的&#xff01;只要花时间学习&#xff0c;每天都是进步的&#xff0c;这些进步可能你现在看不到&#xff0c;但是不要小瞧了积累效应&#xff0c;30天&#xff0c;60天&#…...

【看门狗】我说的是定时器不是狗啊

单片机在运行中死机了&#xff0c;你或许只能按2下电源键&#xff08;重启&#xff09;或1下复位键。 这里简单说一下重启和复位&#xff1a; 从RESET引脚复位&#xff0c;只有MCU复位。而外设看情况&#xff0c;有的可能会有MCU同步复位或者重新初始化。也有可能一些保持复位…...

24万字智慧城市顶层设计及智慧应用解决方案

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。部分资料内容&#xff1a; 4.8 机房消防系统 4.8.1消防系统概况 根据本工程机房消防系统的特殊要求&#xff0c;整个消防系统由火灾报警系统、消防联动系统和气体灭火系统三部…...

跨境电商卖家工具——跨境卫士内容介绍

一、简介 跨境卫士是一款集合多种跨境电商工具的综合软件&#xff0c;由知名跨境电商服务商跨境通开发。跨境卫士可以帮助卖家完成海外物流管理、订单处理、报关报税、市场营销等多项任务&#xff0c;同时还提供数据分析、客户服务、运营管理等一系列支持功能&#xff0c;方便卖…...

Redis 常用基本命令

关于 redis 的常用基本命令 目录 关于 redis 的常用基本命令 1. 关于 key 的操作 2. HyperLogLog 求近似基数 3. 排序相关命令 4. Limit 限制查询 1. 关于 key 的操作 判断某个 key 是否存在 # 格式: exists key exists name# 存在name 返回1 # 不存在name 返回0 查找或…...

【Leetcode】队列的性质与应用

文章目录225. 用队列实现栈示例&#xff1a;提示&#xff1a;分析&#xff1a;题解&#xff1a;622. 设计循环队列示例&#xff1a;提示&#xff1a;分析&#xff1a;题解&#xff1a;225. 用队列实现栈 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...