Java泛型机制
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉
🍎个人主页:Leo的博客
💞当前专栏:每天一个知识点
✨特色专栏: MySQL学习
🥭本文内容:Java泛型机制
🖥️个人小站 :个人博客,欢迎大家访问
📚个人知识库: 知识库,欢迎大家访问
1. 为什么会有泛型
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
引入泛型的意义在于:
- 适用于多种数据类型执行相同的代码
我们通过一个例子来阐述,先看下下面的代码:
-
泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
看下这个例子:
private static int add(int a, int b) {System.out.println(a + "+" + b + "=" + (a + b));return a + b;}private static float add(float a, float b) {System.out.println(a + "+" + b + "=" + (a + b));return a + b;}private static double add(double a, double b) {System.out.println(a + "+" + b + "=" + (a + b));return a + b;}
如果没有泛型,要实现不同类型的加法,每种类型都需要重载一个add方法;通过泛型,我们可以复用为一个方法:
private static <T extends Number> double add(T a, T b) {System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));return a.doubleValue() + b.doubleValue(); }
List list = new ArrayList(); list.add("Leo"); list.add(100d); list.add(new Person());
我们在使用上述
list
中,list
中的元素都是Object
类型(无法约束其中的类型),所以在取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现java.lang.ClassCastException
异常。引入泛型,它将提供类型的约束,提供编译前的检查:
List<String> list = new ArrayList<String>();// list中只能放String, 不能放其它类型的元素
如上代码所示,在没有泛型之前类型的检查和类型的强转都必须由我们程序员自己负责,一旦我们犯了错(谁还能不犯错?),就是一个运行时崩溃等着我们。那时候我们就会抱怨了:***编译器,毛也检查不出来,我把一个
Integer
类型的对象强行转换成String
类型你在编译的时候也不告诉我,害的我程序运行时崩溃了,这个月奖金没了!
2. 泛型的作用对象
2.1 泛型集合
泛型本质上是提供类型的“类型参数”,也就是参数化类型。我们可以为类、接口或方法指定一个类型参数,通过这个参数限制操作的数据类型,从而保证类型转换的绝对安全。
例 1
下面将结合泛型与集合编写一个案例实现图书信息输出。
1)首先需要创建一个表示图书的实体类 Book,其中包括的图书信息有图书编号、图书名称和价格。Book 类的具体代码如下:
package com.Leo.generics;/*** @author : Leo* @version 1.0* @date 2023/8/23 22:22* @description : Book*/
public class Book {private int Id; // 图书编号private String Name; // 图书名称private int Price; // 图书价格public Book(int id, String name, int price) { // 构造方法this.Id = id;this.Name = name;this.Price = price;}public String toString() { // 重写 toString()方法return this.Id + ", " + this.Name + "," + this.Price;}
}
2)使用 Book 作为类型创建 Map 和 List 两个泛型集合,然后向集合中添加图书元素,最后输出集合中的内容。具体代码如下:
package com.Leo.generics;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author : Leo* @version 1.0* @date 2023/8/23 22:22* @description :*/
public class Demo2 {public static void main(String[] args) {// 创建3个Book对象Book book1 = new Book(1, "灰姑娘", 8);Book book2 = new Book(2, "大猩猩", 12);Book book3 = new Book(3, "童话故事", 22);// 定义泛型 Map 集合Map<Integer, Book> books = new HashMap<>();// 将第一个 Book 对象存储到 Map 中books.put(1001, book1);// 将第二个 Book 对象存储到 Map 中books.put(1002, book2);// 将第三个 Book 对象存储到 Map 中books.put(1003, book3);System.out.println("泛型Map存储的图书信息如下:");for (Integer id : books.keySet()) {// 遍历键System.out.print(id + "——");// 不需要类型转换System.out.println(books.get(id));}// 定义泛型的 List 集合List<Book> bookList = new ArrayList<>();bookList.add(book1);bookList.add(book2);bookList.add(book3);System.out.println("泛型List存储的图书信息如下:");for (int i = 0; i < bookList.size(); i++) {// 这里不需要类型转换System.out.println(bookList.get(i));}}}
在该示例中,第 21行代码创建了一个键类型为 Integer、值类型为 Book 的泛型集合,即指明了该 Map 集合中存放的键必须是 Integer 类型、值必须为 Book 类型,否则编译出错。在获取 Map 集合中的元素时,不需要将books.get(id);
获取的值强制转换为 Book 类型,程序会隐式转换。在创建 List 集合时,同样使用了泛型,因此在获取集合中的元素时也不需要将bookList.get(i)
代码强制转换为 Book 类型,程序会隐式转换。
执行结果如下:
2.2 泛型类
除了可以定义泛型集合之外,还可以直接限定泛型类的类型参数。语法格式如下:
public class class_name<data_type1,data_type2,…>{}
其中,class_name 表示类的名称,data_ type1 等表示类型参数。Java 泛型支持声明一个以上的类型参数,只需要将类型用逗号隔开即可。
泛型类一般用于类中的属性类型不确定的情况下。在声明属性时,使用下面的语句:
private data_type1 property_name1;private data_type2 property_name2;
该语句中的 data_type1 与类声明中的 data_type1 表示的是同一种数据类型。
在实例化泛型类时,需要指明泛型类中的类型参数,并赋予泛型类属性相应类型的值。例如,下面的示例代码创建了一个表示学生的泛型类,该类中包括 3 个属性,分别是姓名、年龄和性别。
package com.Leo.generics;public class Student<N, A, S> {/** 姓名 */private N name;/** 年龄 */private A age;/** 性别 */private S sex;// 创建类的构造函数public Student(N name, A age, S sex) {this.name = name;this.age = age;this.sex = sex;}/** 下面是上面3个属性的setter/getter方法*/public N getName() {return name;}public void setName(N name) {this.name = name;}public A getAge() {return age;}public void setAge(A age) {this.age = age;}public S getSex() {return sex;}public void setSex(S sex) {this.sex = sex;}
}
接着创建测试类。在测试类中调用 Stu 类的构造方法实例化 Stu 对象,并给该类中的 3 个属性赋予初始值,最终需要输出学生信息。测试类的代码实现如下:
package com.Leo.generics;/*** @author : Leo* @version 1.0* @date 2023/8/23 22:33* @description :*/
public class Demo03 {public static void main(String[] args) {Student<String, Integer, Character> stu = new Student<>("Leo", 22, '男');String name = stu.getName();Integer age = stu.getAge();Character sex = stu.getSex();System.out.println("学生信息如下:");System.out.println("学生姓名:" + name + ",年龄:" + age + ",性别:" + sex);}
}
运行结果:
在该程序的
Student
类中,定义了 3 个类型参数,分别使用 N、A 和 S 来代替,同时实现了这 3 个属性的 setter/getter 方法。在主类中,调用 Stu 类的构造函数创建了Student
类的对象,同时指定 3 个类型参数,分别为 String、Integer 和 Character。在获取学生姓名、年龄和性别时,不需要类型转换,程序隐式地将 Object 类型的数据转换为相应的数据类型。
注意:
-
- 泛型的类型参数只能是类类型,不能是简单类型。
-
- 不能对确切的泛型类型使用 instanceof 操作。如下面的操作是非法的,编译时会出错。
2.3 泛型方法
在此之前,我们所使用的泛型都是应用于整个类上。泛型同样可以在类中包含参数化的方法,而方法所在的类可以是泛型类,也可以不是泛型类。也就是说,是否拥有泛型方法,与其所在的类是不是泛型没有关系。
泛型方法使得该方法能够独立于类而产生变化。如果使用泛型方法可以取代类泛型化,那么就应该只使用泛型方法。另外,对一个 static
的方法而言,无法访问泛型类的类型参数。因此,如果 static
方法需要使用泛型能力,就必须使其成为泛型方法。
- 自定义的标识符(T、V、E)来代表一个类型,用
< >
括住,放在方法返回值前面。可以被用到形参声明、方法返回值、方法定义中的变量声明和类型转换。 - 泛型方法使得该泛型方法的类型参数独立于类而产生变化。泛型方法和泛型类没有关系。
- 泛型方法的类型参数,一般情况下都是被推断inference出来。更具体地讲,只能被形参或返回值推断出来,当形参和返回值用了同一个类型参数时,二者推断出来的类型必须一样、或者符合多态。
- 形参的类型参数通过实参确定;返回值的类型参数通过方法返回值赋值的对象确定。这也就是 类型参数推断 。
- 当形参的类型参数和返回值的类型参数是同一个时,优先使用形参的推断。因为返回值的类型参数的推断是一种拖延行为。
- 类的成员方法可以使用定义泛型类的类型参数(注意,这种方法不是泛型方法,只不过使用了类型参数而已);而类的静态方法不可以使用泛型类的类型参数,这是因为只有当创建泛型类对象时类型参数才会被具体类型确定,也就是说,泛型类的类型参数是与对象相关的。那么,很自然地,作为一个static方法肯定不可以使用泛型类的类型参数。 static方法想用到泛型只能将其定义为泛型方法。
定义泛型方法的语法格式如下:
[访问权限修饰符] [static] [final] <类型参数列表> 返回值类型 方法名([形式参数列表])
例如:
public static <T> List find(Class<T> cs,int userId){}
一般来说编写 Java 泛型方法,其返回值类型至少有一个参数类型应该是泛型,而且类型应该是一致的,如果只有返回值类型或参数类型之一使用了泛型,那么这个泛型方法的使用就被限制了。下面就来定义一个泛型方法
,具体介绍泛型方法的创建和使用。
public static <T> void List(T book) { // 定义泛型方法if (book != null) {System.out.println(book);}}public static void main(String[] args) {Book stu = new Book(1, "Leo学Java", 28);List(stu);// 调用泛型方法}
2.4 泛型数组
其实在很多文档中都有提到这个概念,于是我翻阅了Sun文档,经过查看Sun的说明文档,在java中是**”不能创建一个确切的泛型类型的数组”**的。
也就是说下面的这个例子是不可以的:
List<String>[] ls = new ArrayList<String>[10];
而使用通配符创建泛型数组是可以的,如下面这个例子:
List<?>[] ls = new ArrayList<?>[10];
这样也是可以的:
List<String>[] ls = new ArrayList[10];
3. 泛型通配符
我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V 等等,这些通配符又都是什么意思呢?
1. 常用的 T,E,K,V,?
本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,? 是这样约定的:
- ? 表示不确定的 java 类型
- T
(type)
表示具体的一个java类型 - K V
(key value)
分别代表java键值中的Key Value - E
(element)
代表Element
2. ? 和 T 的区别
?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ? 不行,比如如下这种 :
// 可以
T t = operate();// 不可以
? car = operate();
简单总结下:
T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。
4. 总结
本文零碎整理了下 JAVA 泛型中的一些点,不是很全,仅供参考。如果文中有不当的地方,欢迎指正。
相关文章:

Java泛型机制
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏:每天一个知识点 ✨特色专栏:…...

Linux CentOS安装抓包解包工具Wireshark图形化界面
1.Wireshark介绍 Wireshark 是一个开源的网络协议分析工具,它能够捕获和分析网络数据包,提供深入的网络故障排除、网络性能优化和安全审计等功能。它支持跨多个操作系统,包括 Windows、macOS 和 Linux。 2.Wireshark主要使用方法 捕获数据…...

虹科分享 | 温度边缘效应对冻干成品含水量的影响(下)——优化和总结
上一篇文章中介绍到借助虹科Ellab的温度记录仪观察到由于冻干机壁面温度的影响,形成的边缘效应导致同一隔板的不同区域冻干饼块的干燥程度不均匀,含水量不同。 06 初次试验结果: 二次干燥中的产品温度显示: 放置在搁板中间的产品…...

ATF(TF-A)安全通告 TFV-1 (CVE-2016-10319)
安全之安全(security)博客目录导读 ATF(TF-A)安全通告汇总 目录 一、ATF(TF-A)安全通告 TFV-1 (CVE-2016-10319) 二、CVE-2016-10319 一、ATF(TF-A)安全通告 TFV-1 (CVE-2016-10319) Title 错误的固件更新SMC可能导致意外的大数据拷贝到安全内存中 CVE ID CVE-2016-10319 …...

说说我最近筛简历和面试的感受。。
大家好,我是鱼皮。 都说现在行情不好、找工作难,但招人又谈何容易?! 最近我们公司在招开发,实习社招都有。我收到的简历很多,但认真投递的、符合要求的却寥寥无几,而且都是我自己看简历、选人…...

Mysql /etc/my.cnf参数详解(一)
[mysqld] #server相关 server_id176452388 //每个MySQL服务器都需要具有唯一的server_id值 super_read_only 0 //不开启只读,在slave节点会开启即super_read_only 1 port 3306 //指定了Mysql开放的端口; default-storage-engine InnoDB skip-name…...

用最少数量的箭引爆气球【贪心算法】
用最少数量的箭引爆气球 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着 x 轴从不同点 完全垂直 地…...

Matlab论文插图绘制模板第109期—特征渲染的标签气泡散点图
在之前的文章中,分享了Matlab标签散点图的绘制模板: 特征渲染的标签散点图: 进一步,再来分享一下特征渲染的标签气泡散点图的绘制模板,从而可以再添加一个维度的信息。 先来看一下成品效果: 特别提示&…...

音视频 ffplay命令播放媒体
播放本地文件 ffplay -window_title "test time" -ss 2 -t 10 -autoexit test.mp4 ffplay buweishui.mp3播放网络流 ffplay -window_title "rtmp stream" rtmp://202.69.69.180:443/webcast/bshdlive-pc强制解码器 mpeg4解码器:ffplay -vco…...

使用Fiddler模拟网络
Fiddler已经预置提供了模拟Modem速度的选项,其位置位于: Rules->Performances->Simulate Modem Speeds 勾选该选项后,所有通过Fiddler代理的流量都会变得用56k modem上网一般。 要直观观察限速后的效果,最好使用运行在浏览…...

【Axure高保真原型】多图表动态切换
今天和大家分享多图表动态切换的原型模板,点击不同的图标可以动态切换对应的表,包括柱状图、条形图、饼图、环形图、折线图、曲线图、面积图、阶梯图、雷达图;而且图表数据可以在左侧表格中动态维护,包括增加修改和删除࿰…...

笔试题-访问控制修饰符范围
...

基于飞腾芯片的设计与调试入门指导
一、啥是自主可控 国产CPU现在厂家细算起来其实有很多,现在华为、小米也在做自己的CPU,瑞芯微、全志等的SoC现在也是广泛应用。但是真正能叫做自主可控的CPU厂商,只有6家。那啥是自主可控?首先来不严谨的讲下现在数字芯片是怎么做的设计。FPGA大家都知道,可以通过Verilog…...

了解 HarmonyOS
引言 在开始 HarmonyOS 开发之前,了解其背景、特点和架构是非常重要的。本章将为你提供一个全面的 HarmonyOS 概览。 目录 什么是 HarmonyOS HarmonyOS 的发展历程 HarmonyOS 的特点 HarmonyOS 的架构 HarmonyOS 与其他操作系统的比较 1. 什么是 HarmonyOS …...

【校招VIP】产品面试之面试官的真实意图
考点介绍: 大厂面试时,面试官提出的问题除了了解经历和想法外,最看重的是思维逻辑能力、团队协作能力和协调能力。 『产品面试之面试官的真实意图』相关题目及解析内容可点击文章末尾链接查看! 一、考点题目 1. 你遇到的最大的…...

实现远程访问Linux堡垒机:通过JumpServer系统进行安全的服务器管理
文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。JumpS…...

Go 1.21新增的 maps 包详解
maps 包提供了几个非常有用的用于操作 map 类型(任何类型的 map)的函数,本文接下来详细介绍下这几个函数。 maps.Clone 定义如下: func Clone[M ~map[K]V, K comparable, V any](m M) M 返回 m 的一个副本,因为新的…...

《向量数据库指南》——腾讯云向量数据库(Tencent Cloud VectorDB) SDK 正式开源
腾讯云向量数据库 SDK 宣布正式开源。根据介绍,腾讯云向量数据库(Tencent Cloud VectorDB)的 Python SDK 与 Java SDK 是基于数据库设计模型,遵循 HTTP 协议,将 API 封装成易于使用的 Python 与 Java 函数或类,为开发者提供了更加友好、更加便捷的数据库使用和管理方式。…...
Tutorial: Mathmatical Derivation of Backpropagation
目录 1. 概要 2. Gradient Descent 3. Chain rule 3.1 单变量基本链式法则 3.2 单变量全微分链式法则 3.3 小贴士:微分、导数、导函数是什么关系? 4. What and why backpropagation? 5. Backpropagation for a simple neural network 5.1 基于…...

如何在 Linux 中设置 SSH 无密码登录
SSH(Secure SHELL)是一种开源且可信的网络协议,用于登录远程服务器以执行命令和程序。 它还用于使用安全复制 (SCP) 命令和 rsync 命令通过网络将文件从一台计算机传输到另一台计算机。 在本文[1]中,我们将向您展示如何在基于 RHE…...

什么时候用增量式PID,什么时候用位置式PID
PID控制器原理: 增量式PID: 位置式PID: 什么时候用位置式PID,什么时候用增量式PID: 在设计PID控制器时,应该考虑下什么时候用增量式,什么时候用位置式。需要看控制器输出u与控制目标之间的关系…...

Go语言入门记录:从基础到变量、函数、控制语句、包引用、interface、panic、go协程、Channel、sync下的waitGroup和Once等
程序入口文件的包名必须是main,但主程序文件所在文件夹名称不必须是main,即我们下图hello_world.go在main中,所以感觉package main写顺理成章,但是如果我们把main目录名称改成随便的名字如filename也是可以运行的,所以…...

位运算进阶操作
位运算的进阶操作,适合做题的时候用,共10点 1.通过位运算与特定的位模式进行掩码操作,可以提取、设置或清除特定的位信息。例如,我们可以使用位掩码来检查一个数的二进制表示中特定位置是否为1。 bool checkBit(int num, int po…...

sql:SQL优化知识点记录(四)
(1)explain之ref介绍 type下的ref是非唯一性索引扫描具体的一个值 ref属性 例如:ti表先加载,const是常量 t1.other_column是个t1表常量 test.t1.ID:test库t1表的ID字段 t1表引用了shared库的t2表的col1字段&#x…...

Java----Sentinel持久化规则启动
java -jar -Dnacos.add8848 你的sentinel源码修改包.jar 前期准备: 1.引入依赖 在order-service中引入sentinel监听nacos的依赖: <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</…...

Java版工程行业管理系统源码-专业的工程管理软件- 工程项目各模块及其功能点清单
鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工程管…...

无涯教程-Android - Grid View函数
Android GridView在二维滚动网格(行和列)中显示项目,并且网格项目不一定是预定的,但它们会使用ListAdapter自动插入到布局中 Grid View - Grid view ListView 和 GridView 是 AdapterView 的子类,可以通过将它们绑定到 Adapter 来填充&#x…...

【第四阶段】kotlin语言的解构语法过滤元素
1.list集合的解构操作 package Stage4fun main() {val list listOf("java","kotlin","c")//元素解构var(v1,v2,v3)listprint("v1$v1,v2$v2,v3$v3") }执行结果 2.将上述代码转化为Java代码 使用Java 代码需要大量书写 3.解构过滤元…...

和24考研说拜拜,不考研读中外合作办学硕士——人大女王金融硕士
23考研失利同学,大多都会有这样的疑虑,是再试一次还是选择其他方式呢?其实,并不用执着于全国联考,中外合作办学硕士或许更适合你。近年来,经济迅速发展,经济全球化不断扩大,金融方向…...

https比http安全在哪
HTTPS(Hypertext Transfer Protocol Secure)是HTTP的安全版本,它在HTTP的基础上添加了安全性和加密机制。以下是HTTPS相对于HTTP的主要安全性优势: 数据加密:HTTPS使用TLS(Transport Layer Security&#x…...