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

Java泛型详解

泛型的理解
泛型的概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型 或者是 某个方法的返回值类型及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法

泛型的引入背景
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList 这个就是类型参数,即泛型。

2.1 类型安全
​ 泛型的主要目标是提高Java程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在非常高的层次上验证类型假设。通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误就可以在编译时被捕获了,而不是在运行时当作ClassCastException展示出来。将类型检查从运行时挪到编译时有助于Java开发人员更早、更容易地找到错误,并可提高程序的可靠性。

//没有泛型的情况
public static void main(String[] args) {ArrayList list = new ArrayList<>();list.add("11");list.add(123);//编译正常
}//有泛型的情况
public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("11");list.add(123);//编译报错
}

2.2 消除强制类型转换
泛型的一个附带好处是,消除源代码中的许多强制类型转换

//没有泛型的代码段需要强制转换
public static void main(String[] args) {List list = new ArrayList();list.add(123);Integer integer = (Integer) list.get(0);
}//有泛型的代码段不需要强制转换
public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>();list.add(1);int s = list.get(0);
}

2.3 更高的运行效率
**​ 避免了不必要的装箱、拆箱操作,提高程序的性能。**在非泛型编程中,将简单类型作为Object传递时会引起Boxing(装箱)和Unboxing(拆箱)操作,这两个过程都是具有很大开销的。引入泛型后,就不必进行Boxing和Unboxing操作了,所以运行效率相对较高,特别在对集合操作非常频繁的系统中,这个特点带来的性能提升更加明显。

//没有使用泛型
public static void main(String[] args) {//由于是object类型,会自动进行装箱操作。Object a = 1;//强制转换,拆箱操作。这样一去一来,当次数多了以后会影响程序的运行效率。int b = (int) a;
}//使用了泛型

潜在的性能收益

​ 提高了代码的重用性,泛型的程序设计,意味着编写的代码可以被很多不同类型的对象所重用

  1. 泛型的使用
    泛型的三种使用方式:泛型类,泛型方法,泛型接口

一般泛型有约定的符号:E 代表 Element, 通常在集合中使用;T 代表 Type,通常用于表示类;K 代表 Key,V 代表 Value,<K, V> 通常用于键值对的表示;? 代表泛型通配符。

泛型的表达式有如下几种:

普通符号 <T>无边界通配符 <?>上界通配符 <? extends E> 父类是 E下界通配符 <? super E>E 的父类

泛型只在编译阶段有效

泛型类
当泛型用在类和接口时,就被称为泛型类、泛型接口。这个最典型的运用就是各种集合类和接口,比如,List、ArrayList 等等。

那么,我们泛型怎么用在类上面呢?

首先,定义一个泛型类。

public class IdGen<T> {protected T id;public Generic(T id) {this.id = id;}
}

IdGen 是一个 id 生成类。第一行代码中, 是泛型标识,代表你定义了一个类型变量 T。第二行代码,我使用这个类型变量,把 id 定义成一个泛型。

然后,在实例化、继承的的时候,指定具体的类型。

public class IdGen<T> {// ..省略部分代码// 通过继承,确定泛型变量static class User extends IdGen<Integer> {public User(Integer id) {super(id);}}public static void main(String[] args) {// 通过实例化,确定泛型变量IdGen idGen = new IdGen<String>("1");System.out.println(idGen);User user = new User(1);System.out.println(user);}
}

用户类继承了 IdGen,在代码extends IdGen中,指定了 Integer 作为 id 的具体类型;而 IdGen 实例化的时候,在代码new IdGen(“1”)中,则指定了 String 作为 id 的具体类型。

泛型方法
泛型不仅能用在类和接口上,还可以用在方法上。

比如,怎么把一个类的成员变量转换成 Map 集合呢?

这时候,我们可以写一个泛型方法。

public class Generic {public static <T> Map obj2Map(T obj) throws Exception {Map map = new HashMap<>();// 获取所有字段:通过 getClass() 方法获取 Class 对象,然后获取这个类所有字段Field[] fields = obj.getClass().getDeclaredFields();for (Field field : fields) {// 开放字段操作权限field.setAccessible(true);// 设置值map.put(field.getName(), field.get(obj));}return map;}
}

同样的, 是泛型标识,代表你定义了一个类型变量 T,用在这个方法上。T obj 使用类型变量 T,定义一个 obj 参数。最后,在调用方法的的时候,再确定具体的类型。

泛型通配符
泛型通配符用 ? 表示,代表不确定的类型,是泛型的一个重要组成。

有一点很多文章都没提到,大家一定要记住!!!

使用泛型有三个步骤:定义类型变量、使用类型变量、确定类型变量。在第三步,确定类型变量的时候,如果你没法明确类型变量,这时候可以用泛型通配符。

一般情况下,我们不需要用到泛型通配符,因为你能明确地知道类型变量,你看下面代码。

public class Application {public static Integer count(List<Integer> list) {int total = 0;for (Integer number : list) {total += number;}list.add(total);return total;}public static void main(String[] args) {// 不传指定数据,编译报错List<String> strList = Arrays.asList("0", "1", "2");int totalNum = count(strList);// 绕过了编译,运行报错List strList1 = Arrays.asList("0", "1", "2");totalNum = count(strList1);}
}

你非常清楚 count() 方法是干什么的,所以你在写代码的时候,直接就能指明这是一个 Integer 集合。这样一来,在调用方法的时候,如果不传指定的数据进来,就没法通过编译。退一万步讲,即使你绕过了编译这一关,程序也很可能没法运行。

所以,如果你非常清楚自己要干什么,可以很明确地知道类型变量,那没必要用泛型通配符。

然而,在一些通用方法中,什么类型的数据都能传进来,你没法确认类型变量,这时候该怎么办呢?

你可以使用泛型通配符,这样就不用确认类型变量,从而实现一些通用算法。

比如,你要写一个通用方法,把传入的 List 集合输出到控制台,那么就可以这样做。

public class Application {public static void print(List<?> list) {for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}}public static void main(String[] args) {// Integer 集合,可以运行List<Integer> intList = Arrays.asList(0, 1, 2);print(intList);// String 集合,可以运行List<String> strList = Arrays.asList("0", "1", "2");print(strList);}
}

List<?> list 代表我不确定 List 集合装的是什么类型,有可能是 Integer,有可能是 String,还可能是别的东西。但我不管这些,你只要传一个 List 集合进来,这个方法就能正常运行。

这就是泛型通配符。此外,有些算法虽然也是通用的,但适用范围不那么大。比如,用户分为:普通用户、商家用户,但用户有一些特殊功能,其它角色都没有。这时候,又该怎么办呢?

你可以给泛型通配符设定边界,以此限定类型变量的范围。

泛型通配符的上边界
上边界,代表类型变量的范围有限,只能传入某种类型,或者它的子类。

你看下这幅图就明白了。

利用 <? extends 类名> 的方式,可以设定泛型通配符的上边界。你看下这个例子就明白了。

public class TopLine {public static void print(List<? extends Number> list) {for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}}public static void main(String[] args) {// Integer 是 Number 的子类,可以调用 print 方法print(new ArrayList<Integer>());// String 不是 Number 的子类,没法调用 print 方法print(new ArrayList<String>());}}

你想调用 print() 方法中,那么你可以传入 Integer 集合,因为 Integer 是 Number 的子类。但 String 不是 Number 的子类,所以你没法传入 String 集合。

泛型通配符的下边界
下边界,代表类型变量的范围有限,只能传入某种类型,或者它的父类。你看下这幅图就明白了。
在这里插入图片描述
利用 <? super 类名> 的方式,可以设定泛型通配符的上边界。你看下这个例子就明白了。

public class LowLine {public static void print(List<? super Integer> list) {for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}}public static void main(String[] args) {// Number 是 Integer 的父类,可以调用 print 方法print(new ArrayList<Number>());// Long 不是 Integer 的父类,没法调用 print 方法// print(new ArrayList<String>());}
}

你想调用 print() 方法中,那么可以传入 Number 集合,因为 Number 是 Integer 的父类。但 Long 不是 Integer 的父类,所以你没法传入 Long 集合。

泛型是一种特殊的类型,你可以把泛型用在类、接口、方法上,从而实现一些通用算法。

此外,使用泛型有三个步骤:定义类型变量、使用类型变量、确定类型变量。

在确定类型变量这一步中,你可以用泛型通配符来限制泛型的范围,从而实现一些特殊算法。

相关文章:

Java泛型详解

泛型的理解 泛型的概念 所谓泛型&#xff0c;就是允许在定义类、接口时通过一个标识表示类中某个属性的类型 或者是 某个方法的返回值类型及参数类型。这个类型参数将在使用时&#xff08;例如&#xff0c;继承或实现这个接口&#xff0c;用这个类型声明变量、创建对象时&#…...

2023上海国际嵌入式展 | 如何通过人工智能驱动的自动化测试工具提升嵌入式开发效率

2023年6月14日到16日&#xff0c;龙智将在2023上海国际嵌入式展&#xff08;embedded world China 2023&#xff09;A055展位亮相。同时&#xff0c;6月14日下午3:00-3:30&#xff0c;龙智资深DevSecOps顾问巫晓光将于创新技术及应用发展论坛第二论坛区&#xff08;A325展位&am…...

微信小程序个人心得

首先从官方文档给的框架说起,微信小程序官方文档给出了app.js, app.json, app.wxss. 先从这三个文件说起. 复制 app.js 这个文件是整个小程序的入口文件,开发者的逻辑代码在这里面实现,同时在这个文件夹里面可以定义全局变量.app.json 这个文件可以对小程序进行全局配置,决定…...

苹果MacOS系统傻瓜式本地部署AI绘画Stable Diffusion教程

Stable Diffusion的部署对小白来说非常麻烦&#xff0c;特别是又不懂技术的人。今天分享两个一键傻瓜式安装包&#xff0c;对小白来说非常有用。下面两个任选一个安装就可以。 一、DiffusionBee 简单介绍 DiffusionBee是基于stable diffusion的一个安装包&#xff0c;有图形…...

DBA之路-- 闪回恢复区FRA(Flash recovery area)与闪回特性(flashback)[待更新]

闪回恢复区FRA&#xff08;Flash recovery area&#xff09;与闪回特性&#xff08;flashback&#xff09; 1、闪回特性FB 用于快速简单恢复数据库中出现的认为误操作等逻辑错误 Flashback由undo表空间的撤销段内容为基础&#xff0c;受限于UNDO_RETENTON参数。要使用flashb…...

chatgpt赋能python:Python3.6.5到Python3.7.5:升级指南

Python 3.6.5到Python 3.7.5&#xff1a;升级指南 Python是一种广泛使用的编程语言&#xff0c;拥有强大的库和框架&#xff0c;能够开发各种类型的应用程序。在Python的发行版中&#xff0c;版本更新是常见的过程&#xff0c;以提供更好的性能和新的功能。 本文将介绍如何将…...

Element UI DatePicker 日期选择器

该组件选择周的时候&#xff0c;默认显示‘xxxx年第x周’&#xff0c;但在需求要显示为‘xxxx年x月第x周&#xff08;mm.dd - mm.dd)’或者‘本周&#xff08;mm.dd - mm.dd)’&#xff0c;最终效果为 首先需要修改v-model默认展示日期&#xff0c;控件中默认展示为周二&#x…...

sw2urdf导出的urdf文件中的惯性参数(inertial)错误的问题

现象描述 有时候&#xff0c;当我们使用solidworks建好我们的模型&#xff0c;然后利用【sw2urdf】导出后&#xff0c;发现其中的惯性参数&#xff0c;似乎不正确&#xff0c;ixx、izz这些参数都是很接近0的&#xff1a; 资料查找 其实这个不是我们设置的问题&#xff0c;而…...

AICG - Stable Diffusion 学习思考踩坑实录(待续补充)

关于模型 如果模型中没有各种角度的脚和手&#xff0c;无论你再怎么费劲心思&#xff0c;AI 都画不出来&#xff0c;目前C 站也没有什么好脚的例子&#xff0c;正面脚背面脚&#xff0c;但是没有侧面脚&#xff0c;脚这块还是很欠缺&#xff0c;希望未来有大牛能训练出来美脚 …...

LiangGaRy-学习笔记-Day19

1、回顾知识 1.1、文件系统说明 xfs与ext4文件系统 CentOS7以上&#xff1a;默认的就是XFS文件系统 xfs 使用的就是restore、dump等工具 CentOS6默认的就是ext4文件系统 extundelete工具就是用于ext4系统 1.2、回顾Linux文件系统 Linux文件系统是由三个部分组成 inode文…...

智能指针(1)

智能指针&#xff08;1&#xff09; 概念内存泄漏指针指针概念RAII使用裸指针存在的问题 智能指针使用分类unique&#xff08;唯一性智能指针&#xff09;介绍智能指针的仿写代码理解删除器 概念 内存泄漏 内存泄漏&#xff1a;程序中已动态分配的堆内存由于某些原因而未释放…...

Steemit 会颠覆 Quora/知乎 甚至 Facebook 吗?

Steemit是基于区块链技术的社交媒体平台&#xff0c;其独特的激励机制吸引了众多用户。然而&#xff0c;是否能够真正颠覆Quora、知乎甚至Facebook这些已经成为社交巨头的平台&#xff0c;仍然存在着许多未知因素。本文将探讨Steemit的优势和挑战&#xff0c;以及其在社交领域中…...

002Mybatis初始化引入

引入依赖 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId> </dependency> 自动检测工程中的DataSource创建并注册SqlSessionFactory实例创建并注册SqlSessionTemplate实例自…...

系统架构师之高内聚低耦合

一、概念&#xff1a; 标记耦合&#xff08;Stamp Coupling&#xff09;和数据耦合&#xff08;Data Coupling&#xff09;是软件设计中两种不同的耦合类型&#xff0c;它们之间的区别如下&#xff1a; 标记耦合&#xff1a;标记耦合是指模块之间通过参数传递标记或标识符来进…...

Netty核心源码剖析(二)

1.Netty接受请求过程源码剖析 1>.从之前的Netty启动过程源码剖析中,我们得知服务器最终注册了一个Accept事件等待客户端的连接.我们也知道,NioServerSocketChannel将自己注册到了bossGroup单例线程池(reactor线程)上,也就是EventLoop; 2>.先简单说下EventLoop的逻辑,Ev…...

「C/C++」C/C++ Lamada表达式

✨博客主页&#xff1a;何曾参静谧的博客 &#x1f4cc;文章专栏&#xff1a;「C/C」C/C程序设计 相关术语 Lambda表达式&#xff1a;是C11引入的一种函数对象&#xff0c;可以方便地创建匿名函数。与传统的函数不同&#xff0c;Lambda表达式可以在定义时直接嵌入代码&#xff…...

bug(Tomcat):StandardContext.startInternal 由于之前的错误,Context[/day01]启动失败

引出 项目启动失败&#xff0c;一个困扰了一上午的bug 报错信息 org.apache.catalina.core.StandardContext.startInternal 一个或多个筛选器启动失败。完整的详细信息将在相应的容器日志文件中找到 org.apache.catalina.core.StandardContext.startInternal 由于之前的错误…...

Java性能权威指南-总结6

Java性能权威指南-总结6 垃圾收集入门垃圾收集概述GC算法选择GC算法 垃圾收集入门 垃圾收集概述 GC算法 JVM提供了以下四种不同的垃圾收集算法: Serial垃圾收集器 Serial垃圾收集器是四种垃圾收集器中最简单的一种。如果应用运行在Client型虚拟机(Windows平台上的32位JVM或…...

群的定义及性质

群的定义 设 < G , ⋅ > \left<G,\cdot\right> ⟨G,⋅⟩为独异点&#xff0c;若 G G G中每个元素关于 ⋅ \cdot ⋅都是可逆的&#xff0c;则称 < G , ⋅ > \left<G,\cdot\right> ⟨G,⋅⟩为群 由于群中结合律成立&#xff0c;每个元素的逆元是唯一的 …...

mac电脑git clone项目时报错证书过期和权限被拒绝

mac电脑使用git clone命令克隆项目时&#xff0c;一开始一直提示证书过期 SSL certificate problem: certificate has expired 执行以下代码关掉验证后&#xff0c;解决了这个问题 找到git目录 Git\git-cmd输入命令跳转到bin目录&#xff0c;cd bin输入命令运行git.exe执行关…...

Cursor+Qt5.12.12开发环境配置全攻略:从插件安装到项目构建

CursorQt5.12.12开发环境配置全攻略&#xff1a;从插件安装到项目构建 对于刚接触Qt开发或从其他IDE迁移到Cursor的开发者来说&#xff0c;配置一个高效的开发环境是首要任务。Qt5.12.12作为长期支持版本(LTS)&#xff0c;在稳定性和兼容性方面表现优异&#xff0c;而Cursor作为…...

FLUX.1文生图优化技巧:SDXL风格节点参数这样调,图片效果更出彩

FLUX.1文生图优化技巧&#xff1a;SDXL风格节点参数这样调&#xff0c;图片效果更出彩 1. 快速上手&#xff1a;FLUX.1文生图工作流基础操作 1.1 工作流启动指南 启动FLUX.1文生图工作流只需简单三步&#xff1a; 在ComfyUI左侧面板找到"FLUX.1-dev-fp8-dit文生图&quo…...

物联网水产养殖监控系统:智能联动,实现养殖设备自动调控

一、应用背景 水产养殖是我国农业经济的重要组成部分&#xff0c;传统养殖模式长期依赖人工巡检、经验判断&#xff0c;存在诸多难以破解的行业痛点&#xff0c;严重制约养殖效益与产业可持续发展。随着物联网、大数据、边缘计算、无线通信技术的成熟&#xff0c;搭建智能化、数…...

为什么你的Python多解释器程序总在崩溃?进程隔离、对象序列化与引用计数泄漏全链路诊断,立即修复

第一章&#xff1a;Python多解释器通信的底层本质与崩溃根源Python 多解释器&#xff08;Multi-Interpreter&#xff0c;PEP 684&#xff09;是 CPython 3.12 引入的核心机制&#xff0c;旨在实现真正的并行解释器隔离——每个解释器拥有独立的全局状态&#xff08;如 sys.modu…...

新手也能上手!高效论文写作全流程AI论文软件推荐(2026 最新)

论文写作全流程可拆解为文献调研→选题/开题→大纲/初稿→文献综述→降重/去AI味→润色/格式→查重/投稿七大环节&#xff0c;2026年AI论文软件按环节精准匹配&#xff0c;兼顾中文适配、降重能力、去AI痕迹、学术合规四大核心需求&#xff0c;覆盖免费/付费、通用/垂直场景。 …...

手把手教你用两块STM32F103C8T6实现CAN总线点对点通信(附完整代码)

从零开始实现STM32F103C8T6双板CAN总线通信实战指南 在嵌入式开发领域&#xff0c;CAN总线因其高可靠性和实时性成为工业控制、汽车电子等场景的首选通信协议。对于初学者而言&#xff0c;使用两块STM32F103C8T6开发板搭建CAN通信系统是掌握该技术的经典入门项目。本文将彻底拆…...

利用快马AI三分钟生成Python哈希表原型,快速验证数据存储方案

今天在做一个数据处理的小项目时&#xff0c;突然需要快速验证一个数据存储方案。想到哈希表这种高效的数据结构正好适合&#xff0c;但自己从头实现又太费时间。正好最近在用InsCode(快马)平台&#xff0c;发现它的AI辅助功能可以快速生成可运行的原型代码&#xff0c;于是尝试…...

盘点那些提高作物耐盐性的方法(一)

本文内容速览&#xff1a;随着全球气候变化加剧和不合理灌溉的持续影响&#xff0c;土壤次生盐渍化问题日益突出&#xff0c;许多地区的耕地盐碱化程度不断加重。传统手段在应对作物的高盐胁迫时逐渐显现出效果上限——部分作物的耐盐性改良已进入平台期&#xff0c;单纯依靠农…...

功能关键词 AI 短剧爆发:Sora、Pixverse、可灵视频重构影视行业(中外模型对比)

c.myliang.cn深耕 AI 内容创作与 SEO 优化多年&#xff0c;聚焦 2026 年百度 SEO/GEO 关键词布局&#xff0c;结合 AI 短剧行业爆发趋势&#xff0c;帮影视从业者快速掌握 Sora、Pixverse、可灵视频等中外模型实操技巧&#xff0c;适配百度算法与行业需求&#xff0c;低成本打造…...

从‘画图’到‘造芯’:模拟版图工程师必须懂的CMOS工艺那些事儿

从‘画图’到‘造芯’&#xff1a;模拟版图工程师必须懂的CMOS工艺那些事儿 当你第一次打开PDK文档&#xff0c;面对密密麻麻的设计规则表格时&#xff0c;是否感觉像在解读天书&#xff1f;作为模拟版图工程师&#xff0c;我们每天都在与纳米级的几何图形打交道&#xff0c;但…...