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;
}//使用了泛型
潜在的性能收益
提高了代码的重用性,泛型的程序设计,意味着编写的代码可以被很多不同类型的对象所重用
- 泛型的使用
泛型的三种使用方式:泛型类,泛型方法,泛型接口
一般泛型有约定的符号: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泛型详解
泛型的理解 泛型的概念 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型 或者是 某个方法的返回值类型及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时&#…...
2023上海国际嵌入式展 | 如何通过人工智能驱动的自动化测试工具提升嵌入式开发效率
2023年6月14日到16日,龙智将在2023上海国际嵌入式展(embedded world China 2023)A055展位亮相。同时,6月14日下午3:00-3:30,龙智资深DevSecOps顾问巫晓光将于创新技术及应用发展论坛第二论坛区(A325展位&am…...
微信小程序个人心得
首先从官方文档给的框架说起,微信小程序官方文档给出了app.js, app.json, app.wxss. 先从这三个文件说起. 复制 app.js 这个文件是整个小程序的入口文件,开发者的逻辑代码在这里面实现,同时在这个文件夹里面可以定义全局变量.app.json 这个文件可以对小程序进行全局配置,决定…...
苹果MacOS系统傻瓜式本地部署AI绘画Stable Diffusion教程
Stable Diffusion的部署对小白来说非常麻烦,特别是又不懂技术的人。今天分享两个一键傻瓜式安装包,对小白来说非常有用。下面两个任选一个安装就可以。 一、DiffusionBee 简单介绍 DiffusionBee是基于stable diffusion的一个安装包,有图形…...
DBA之路-- 闪回恢复区FRA(Flash recovery area)与闪回特性(flashback)[待更新]
闪回恢复区FRA(Flash recovery area)与闪回特性(flashback) 1、闪回特性FB 用于快速简单恢复数据库中出现的认为误操作等逻辑错误 Flashback由undo表空间的撤销段内容为基础,受限于UNDO_RETENTON参数。要使用flashb…...
chatgpt赋能python:Python3.6.5到Python3.7.5:升级指南
Python 3.6.5到Python 3.7.5:升级指南 Python是一种广泛使用的编程语言,拥有强大的库和框架,能够开发各种类型的应用程序。在Python的发行版中,版本更新是常见的过程,以提供更好的性能和新的功能。 本文将介绍如何将…...
Element UI DatePicker 日期选择器
该组件选择周的时候,默认显示‘xxxx年第x周’,但在需求要显示为‘xxxx年x月第x周(mm.dd - mm.dd)’或者‘本周(mm.dd - mm.dd)’,最终效果为 首先需要修改v-model默认展示日期,控件中默认展示为周二&#x…...
sw2urdf导出的urdf文件中的惯性参数(inertial)错误的问题
现象描述 有时候,当我们使用solidworks建好我们的模型,然后利用【sw2urdf】导出后,发现其中的惯性参数,似乎不正确,ixx、izz这些参数都是很接近0的: 资料查找 其实这个不是我们设置的问题,而…...
AICG - Stable Diffusion 学习思考踩坑实录(待续补充)
关于模型 如果模型中没有各种角度的脚和手,无论你再怎么费劲心思,AI 都画不出来,目前C 站也没有什么好脚的例子,正面脚背面脚,但是没有侧面脚,脚这块还是很欠缺,希望未来有大牛能训练出来美脚 …...
LiangGaRy-学习笔记-Day19
1、回顾知识 1.1、文件系统说明 xfs与ext4文件系统 CentOS7以上:默认的就是XFS文件系统 xfs 使用的就是restore、dump等工具 CentOS6默认的就是ext4文件系统 extundelete工具就是用于ext4系统 1.2、回顾Linux文件系统 Linux文件系统是由三个部分组成 inode文…...
智能指针(1)
智能指针(1) 概念内存泄漏指针指针概念RAII使用裸指针存在的问题 智能指针使用分类unique(唯一性智能指针)介绍智能指针的仿写代码理解删除器 概念 内存泄漏 内存泄漏:程序中已动态分配的堆内存由于某些原因而未释放…...
Steemit 会颠覆 Quora/知乎 甚至 Facebook 吗?
Steemit是基于区块链技术的社交媒体平台,其独特的激励机制吸引了众多用户。然而,是否能够真正颠覆Quora、知乎甚至Facebook这些已经成为社交巨头的平台,仍然存在着许多未知因素。本文将探讨Steemit的优势和挑战,以及其在社交领域中…...
002Mybatis初始化引入
引入依赖 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId> </dependency> 自动检测工程中的DataSource创建并注册SqlSessionFactory实例创建并注册SqlSessionTemplate实例自…...
系统架构师之高内聚低耦合
一、概念: 标记耦合(Stamp Coupling)和数据耦合(Data Coupling)是软件设计中两种不同的耦合类型,它们之间的区别如下: 标记耦合:标记耦合是指模块之间通过参数传递标记或标识符来进…...
Netty核心源码剖析(二)
1.Netty接受请求过程源码剖析 1>.从之前的Netty启动过程源码剖析中,我们得知服务器最终注册了一个Accept事件等待客户端的连接.我们也知道,NioServerSocketChannel将自己注册到了bossGroup单例线程池(reactor线程)上,也就是EventLoop; 2>.先简单说下EventLoop的逻辑,Ev…...
「C/C++」C/C++ Lamada表达式
✨博客主页:何曾参静谧的博客 📌文章专栏:「C/C」C/C程序设计 相关术语 Lambda表达式:是C11引入的一种函数对象,可以方便地创建匿名函数。与传统的函数不同,Lambda表达式可以在定义时直接嵌入代码ÿ…...
bug(Tomcat):StandardContext.startInternal 由于之前的错误,Context[/day01]启动失败
引出 项目启动失败,一个困扰了一上午的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,⋅⟩为独异点,若 G G G中每个元素关于 ⋅ \cdot ⋅都是可逆的,则称 < G , ⋅ > \left<G,\cdot\right> ⟨G,⋅⟩为群 由于群中结合律成立,每个元素的逆元是唯一的 …...
mac电脑git clone项目时报错证书过期和权限被拒绝
mac电脑使用git clone命令克隆项目时,一开始一直提示证书过期 SSL certificate problem: certificate has expired 执行以下代码关掉验证后,解决了这个问题 找到git目录 Git\git-cmd输入命令跳转到bin目录,cd bin输入命令运行git.exe执行关…...
【AIGC】Photoshop AI Beta版本安装使用(永久免费)
AIGC 大爆发 Adobe近日宣布,Photoshop(测试版)应用程序发布了生成式AI绘图,这是世界上第一个创意和设计工作流程的副驾驶,为用户提供了一种神奇的新工作方式。生成式AI绘图由Adobe Firefly提供支持,Adobe的…...
01 云原生生态系统解读
云计算的技术革命 互联网时代的历程 云计算到底是什么 云计算历程 云平台的优缺点 优势 稳定性:云平台大量资源,分布式集群部署,保障服务永不宕机,几个9弹性扩展:按需索取,一键秒级开通需要的资源安全性&…...
Java——Java易错选择题复习(2)(计算机网络)
1. 下面关于源端口地址和目标端口地址的描述中,正确的是( ) A. 在TCP/UDP传输段中,源端口地址和目的端口地址是不能相同的 B. 在TCP/UDP传输段中,源端口地址和目的端口地址必须是相同的 C. 在TCP/UDP传输段中ÿ…...
【HTML5系列教程】
《HTML5系列教程》目录大纲: 介绍 内容包括HTML简介、服务器的概念、B/S、C/S软件架构、前端与后端的开发内容、HTML发展历程、浏览器内核介绍、Web标准、WebStorm工具的使用、WebStorm常用快捷键、HTML常用标签 如:文本标签(span)、排版标签(div/p/h…...
二、电压源、电流源、受控源
点我回到目录 目录 理想电压源 理想电流源 受控源 电流源做功问题 电压源做功问题 理想电压源 •定义:两端电压保持定值或一定的时间函数,且电压值与流过它的电流i无关 •特点:理想电压源两端的电压由本身决定,与外电路无关…...
骨传导是哪个意思,推荐几款性能优的骨传导耳机
骨传导耳机是通过头部骨迷路传递声音,而不是直接通过耳膜的振动来传递声音。与传统的入耳式耳机相比,骨传导耳机不会堵耳朵,在跑步、骑车等运动时可以更好的接收外界环境音,保护听力,提升安全性。此外,骨…...
利用Taro打造灵活的移动App架构
最近公司的一些项目需要跨端框架,技术老大选了Taro,实践了一段时间下来,愈发觉得Taro是个好东西,所以在本篇文章中稍微介绍下。 什么是Taro? Taro(或称为Taro框架)是一种用于构建跨平台应用程…...
(转载)基于模拟退火算法的TSP问题求解(matlab实现)
1 理论基础 1.1 模拟退火算法基本原理 模拟退火(simulated annealing,SA)算法的思想最早是由Metropolis等提出的。其出发点是基于物理中固体物质的退火过程与一般的组合优化问题之间的相似性。模拟退火法是一种通用的优化算法,其物理退火过程由以下三部分组成&am…...
splitpcap 使用说明
背景 当PCAP原始文件特别巨大的时候,整个文件直接载入内存是相当耗时的,于是一个简单的想法是将大的PCAP切分成若干小PCAP。对于这个任务,现有工具splitcap是可以完成的。无论是按照主机对、还是按照五元组信息切分,splitcap都会…...
配置docker阿里云镜像加速
默认情况下docker安装镜像文件是从docker官方的镜像中心下载:https://hub.docker.com/ , 有时速度慢,可以通过配置docker阿里云镜像来加速,配置后,就从国内阿里云下载。 注册阿里云用户,登录->工作台-&g…...
网站悬浮窗代码/楚雄seo
现在很多小伙伴们都会用WordPress来发送html邮件,但是对于一些新手朋友们来说,会不知道在WordPress中如何发送HTML邮件,接下来我们就和西部数码小编一起去看看WordPress中发送HTML邮件的方法。方法一,用filter发送HTML邮件发邮件用…...
购彩网站建设/打开app下载
MySQL常用函数(分类别整理)2021-01-30一、数学函数ABS(x) 返回x的绝对值BIN(x) 返回x的二进制(OCT返回八进制,HEX返回十六进制)CEILING(x) 返回大于x的最小整数值EXP(x) 返回值e(自然对数的底)的x次方FLOOR(x) 返回小于x的最大整数值GREATEST(x1,x2,...,xn) 返回集合…...
浙江网站建设广告语/长沙网络推广外包费用
华为Nova品牌主要对标OPPO和vivo,低配高价,其强调以外观、拍摄等性能吸引年轻消费者特别是女性消费者,而荣耀品牌则主打性价比,然而今天其发布的Nova5的性价比较荣耀20还要高,Nova品牌似乎要打性价比牌了,这…...
wordpress建响应网址/可以入侵的网站
BackTrack、exploitdb等 网站被黑事件最新消息 首先给大家报个好消息 Ettercap 工程源码并没有被植入后门 下面是Ettercap 主要开发人员Alberto Ornaghi (ALoR)提供的Ettercap source code的SHA1sum值 These are the SHA1sum from my local copy: 206972046b7cfc4150e5d08e…...
精准引流推广团队/网站优化外包价格
本文链接:http://user.qzone.qq.com/21909166/blog/25评论/阅读(0/12)三个真心的笑容有一個家財萬貫的富翁快死了,死神來接他走,他問死神:「我死後會上天 堂還是地獄?」 死神跟他說:「地獄。」 富翁很不服氣࿰…...
b2c网站怎么做/宁波seo关键词培训
展开全部 顾名思义,缩进就是向里面收缩的意思,比如汉语书写习e68a8462616964757a686964616f31333363373039惯文章每段前面都要空两个字,这就叫首行缩进。段落缩进自然就是指整个段向内收缩了。 那么python语法缩进是什么意思呢? 学…...