Effective Java笔记(32)谨慎并用泛型和可变参数
故事的小黄花 从出生那年就飘着
童年的荡秋千 随记忆一直晃到现在
可变参数( vararg ) 方法(详见第 53 条)和泛型都是在 Java 5 中就有了,因此你可能会期待它们可以良好地相互作用;遗憾的是,它们不能 。 可变参数的作用在于让客户端能够将可变数量的参数传给方法,但这是个技术露底( leaky abstration ):当调用一个可变参数方法时,会创建一个数组用来存放可变参数;这个数组应该是一个实现细节,它是可见的 。因此,当可变参数有泛型或者参数化类型时,编译警告信息就会产生温乱 。
回顾一下第28条,非具体化( non-reifable)类型是指其运行时代码信息比编译时少,并且显然所有的泛型和参数类型都是非具体化的。如果一个方法声明其可变参数为non-reifiable类型,编译器就会在声明中产生一条警告。如果方法是在类型为non-reifiable的可变参数.上调用,编译器也会在调用时发出一条警告信息。这个警告信息类似于:
![]()
当一个参数化类型的变量指向一个不是该类型的对象时,会产生堆污染( heap pollution)。 它导致编辑器的自动生成转换失败,破坏了泛型系统的基本保证 。
举个例子 。 下面的代码是对第 28 条中的代码片段稍加修改而得:

这个方法没有可见的转换,但是在调用一个或者多个参数时会抛出 ClassCastException 异常。 上述最后一行代码中有一个不可见的转换,这是由编译器生成的 。 这个转换失败证明类型安全已经受到了危及,因此将值保存在泛型可变参数数组参数 中是不安全的 。
这个例子引出了 一个有趣的问题:为什么显式创建泛型数组是非法的,用泛型可变参数声明方法却是合法的呢?换句话说,为什么之前展示的方法只产生一条警告,而第 28 条中的代码片段却产生一个错误呢?答案在于,带有泛型可变参数或者参数化类型的方法在实践中用处很大,因此 Java 语言的设计者选择容忍这 一矛盾的存在 。 事实上,Java 类库导出了好几个这样的 方法,包括 Arrays.asList(T ... a )、Collections.addAll (Collection<? super T> C, T . . . elements ),以及 EnumSet.of (E first,E ... rest ) 。 与前面提到的危险方法不一样这些类库方法是类型安全的 。
在 Java 7 之前,带泛型可变参数的方法的设计者,对于在调用处 出错的警告信息一点办法也没有。 这使得这些 API 使用起来非常不愉快 。 用户必须忍受这些警告,要么最好在每处调用点都通过@SuppressWarnings (飞rnchecked”)注解来消除警告(详见第 27条) 。 这么做过于烦琐,而且影响可读性,并且掩盖了反映实际问题的警告 。
在 Java 7 中,增加了 SafeVarargs 注解,它让带泛型 vararg 参数的方法的设计者能够自动禁止客户端的警告 。 本质上,SafeVarargs 注解是通过方法的设计者做出承诺,声明这是类型安全的。 作为对于该承诺的交换,编译器同意不再向该方法的用户发出警告说这些调用可能不安全。
重要的是,不要随意用@ SafeVarargs 对方法进行注解,除非它真正是安全的 。 那么它凭什么确保安全呢?回顾一下,泛型数组是在调用方法的时候创建的,用来保存可变参数 。 如果该方法没有在数组中保存任何值,也不允许对数组的引用转义(这可能导致不被信任的代码访问数组),那么它就是安全的 。 换句话说,如果可变参数数组只用来将数量可变的参数从调用程序传到方法(毕竟这才是可变参数的目的),那么该方法就是安全的 。
值得注意的是,从来不在可变参数的数组中保存任何值,这可能破坏类型安全性 。以下面的泛型可变参数方法为例,它返回了一个包含其参数的数组 。 乍看之下,这似乎是一个方便的小工具 :
static<T> T[] toArray(T... args) {return args;
}
这个方法只是返回其可变参数数组,看起来没什么危险,但它实际上很危险 !这个数组的类型,是由传到方法的参数的编译时类型来决定的,编译器没有足够的信息去做准确的决定 。 因为该方法返回其可变参数数组 ,它会将堆污染传到调用堆枝上 。
下面举个具体的例子 。 这是一个泛型方法,它带有三个类型为 T 的参数,并返回 一个包含两个(随机选择的)参数的数组:

这个方法本身并没有危险,也不会产生警告,除非它 调用了 带有泛型 可 变参数的toArray 方法 。
在编译这个方法时,编译器会产生代码, 创建一个可变参数数组,并将两个 T 实例传到 toArray 。 这些代码配置了 一个类型为 Object[ ]的数组,这是确保能够保存这些实例的最具体的类型,无论在调用时给 pickTwo 传递什么类型的对象都没问题 。toArray 方法只是将这个数组返回给 pickTwo ,反过来也将它返回 给其调用程序,因 此 pickTwo 始终都会返回一个类型为 Object[]的数组 。
允许另一个方法访问一个泛型可变参数数组是不安全的 ,有两种情况例外 : 将数组传给另一个用@ SafeVarargs 正确注解过的可变参数方法是安全的,将数组传给只计算数组内容部分函数的非可变参数方法也是安全的 。
确定何时应该使用 SafeVarargs 注解的规则很简单 : 对于每一个带有泛型可变参数或者参数化类型的方法,都要用@ SafeVarargs 进行注解,这样它的用户就不用承受那些无谓的、令人困惑的编译警报了 。
每当编译器警告你控制 的某个带泛型可变参数的方法可能形成堆污染,就应该检查该方法是否安全 。 这里先提个醒,泛型可变参数方法在下列条件下是安全的 :
- 它没有在可变参数数组中保存任何值 。
- 它没有对不被信任的代码开放该数组(或者其克隆程序) 。
以上两个条件只要有任何一条被破坏,就要立即修正它 。
注意,SafeVarargs 注解只能用在无法被覆盖的方法上,因为它不能确保每个可能的覆盖方法都是安全的 。 在 Java 8 中,该注解只在静态方法和 final实例方法中才是合法的;在 Java 9 中,它在私有的实例方法上也合法了 。
总而言之,可变参数和泛型不能良好地合作,这是因为可变参数设施是构建在顶级数组之上的一个技术露底,泛型数组有不同的类型规则 。 虽然泛型可变参数不是类型安全的,但它们是合法的 。 如果选择编写带有泛型(或者参数化)可变参数的方法,首先要确保该方法是类型安全的,然后用@SafeVarargs 对它进行注解,这样使用起来就不会出现不愉快的情况了 。
相关文章:
Effective Java笔记(32)谨慎并用泛型和可变参数
故事的小黄花 从出生那年就飘着 童年的荡秋千 随记忆一直晃到现在 可变参数( vararg ) 方法(详见第 53 条)和泛型都是在 Java 5 中就有了,因此你可能会期待它们可以良好地相互作用;遗憾的是,它们…...
数据结构——双向链表
双向链表实质上是在单向链表的基础上加上了一个指针指向后面地址 单向链表请参考http://t.csdn.cn/3Gxk9 物理结构 首先我们看一下两种链表的物理结构 我们可以看到:双向在单向基础上加入了一个指向上一个地址的指针,如此操作我们便可以向数组一样操作…...
Declare 关键字在 TypeScript 中如何正确使用?
如果您编写 TypeScript 代码的时间足够长,您就已经看到过declare关键字。但它有什么作用,为什么要使用它? declare关键字告诉 TypeScript 编译器存在一个对象并且可以在代码中使用。 本文解释了声明关键字并通过代码示例展示了不同的用例。 定义 在 TypeScript 中,decl…...
ChatGPT将会成为强者的外挂?—— 提高学习能力
目录 前言 一、提高学习力 🧑💻 1. 快速找到需要的知识 2. 组合自己的知识体系 3. 内化知识技能 二、提问能力❗ 三、思维、创新能力 🌟 1. 批判性思维 1.1 八大基本结构进行批判性提问 1.2 苏格拉底的提问分类方法 2. 结构化思…...
AUTOSAR规范与ECU软件开发(基础篇)1.3 车用控制器软件标准(从OSEK到AUTOSAR)
目录 AUTOSAR的前世与今生 1.1~1.3篇幅小结 AUTOSAR的前世与今生 为了迎合汽车高精度、 高实时性、 高可靠性控制的需要, 嵌入式实时操作系统(Real Time Operating System, RTOS) 逐渐在ECU中使用。与此同时, 由于不同实时操作系统间应用程序接口(Application Programmi…...
R语言5_安装Giotto
环境Ubuntu22/20, R4.1. 已开启科学上网。 第一步,更新服务器环境,进入终端,键入如下命令, apt-get update apt install libcurl4-openssl-dev libssl-dev libxml2-dev libcairo2-dev libgtk-3-dev libhdf5-dev libmagick9-dev …...
centos按用户保存历史执行命令
centos7 按用户记录历史命令的方法 在/etc/profile文件中添加以下代码。 添加完成后执行source /etc/profile 用户重新登录即可发现history被清空了。这时可以去看/usr/share/.history文件夹,该文件夹保存了所有用户每次登录所执行过的的操作记录。 文件路径为 /usr…...
【力扣】61. 旋转链表 <快慢指针>
【力扣】61. 旋转链表(每个节点向右移k个单位) 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。 示例 1: 输入:head [1,2,3,4,5], k 2 输出:[4,5,1,2,3] 示例 2&a…...
编写一个指令(v-focus2end)使输入框文本在聚焦时焦点在文本最后一个位置
项目反馈输入框内容比较多时候,让鼠标光标在最后一个位置,心想什么奇葩需求,后面试了一下,是有点影响体验,于是就有了下面的效果,我目前的项目都是若依的架子,用的是vue2版本。vue3的朋友想要使…...
Virtualbox设置访问外网以及主机和虚拟机互通
参考链接 1、设置使虚拟机访问外网。选中虚拟机,右击选择“设置”。 2、在设置中选择“网络”,然后点击“网卡1”,选择“网络地址转换(NAT)”模式,点击“确定”。 4.此时你的虚拟机就可以访问外网了 5…...
请简述React是什么?React的主要特点有哪些?React中有哪些主要组件?
1、请简述React是什么? React是一个用于构建用户界面的JavaScript库,它由Facebook开发并开源。React的主要特点是其数据驱动和组件化的设计理念。它允许开发者将复杂的界面分解为简单的组件,并将这些组件以数据流的方式组合在一起࿰…...
DevOps最佳实践和工具在本地环境中的概述
引言 最近,我进行了一次网上搜索,以寻找DevOps的概述,尽管有大量的DevOps工具和实践,但我无法找到一个综合的概述。因此,我开始了对DevOps生态系统和最佳实践的梳理,以创建一个整体视图,方便后续研究实践 C…...
kafka和rabbitmq之间的区别以及适用场景
Kafka 和 RabbitMQ 都是流行的消息传递系统,用于实现分布式系统中的消息传递、事件处理和数据流。它们在设计和适用场景上有一些不同,下面详细介绍它们之间的区别和适用场景。 Kafka 特点和优势: 高吞吐量: Kafka 的设计目标是实…...
python——案例15:判断奇数还是偶数
案例15:判断奇数还是偶数numint(input(输入数值:))if(num%2)0: #通过if语句判断print("{0}是偶数".format(num))else: #通过else语句判断print("{0}是奇数".format(num))...
springboot汽车租赁后台java出租客户管理jsp源代码mysql
本项目为前几天收费帮学妹做的一个项目,Java EE JSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 springboot汽车租赁后台 系统有1权限:管理…...
Linux学习之sed删除、追加、插入、更改、读写文件、下一行、打印、退出和seq命令
cat /etc/redhat-release看到操作系统是CentOS Linux release 7.6.1810,uname -r看到内核版本是3.10.0-957.el7.x86_64,sed --version可以看到sed版本是4.2.2。 echo a : 1 : good : g >> sed_daicpnrwq.txt echo b : 2 : well : w >> sed…...
JuiceFS 在多云存储架构中的应用 | 深势科技分享
2020 年末,谷歌旗下 DeepMind 研发的 AI 程序 AlphaFold2 在国际蛋白质结构预测竞赛上取得惊人的准确度,使得 “AI 预测蛋白质结构” 这一领域受到了空前的关注。今天我们邀请到同领域企业,深势科技为大家分享其搭建基础平台时的实践与思考。…...
什么是DNS的缓存?
DNS 缓存是一个临时的数据库,存储在计算机或网络设备(如路由器)上,用于保存最近的 DNS 查询结果。这种缓存机制可以加速后续的相同查询,因为设备可以直接从缓存中提取先前的查询结果,而不需要再次到外部的 …...
smtplib.SMTPHeloError: (500, b‘Error: bad syntax‘)
如果你编写邮件收发工具的时候,有可能会遇到这个问题。这里直接给出解决办法。 目录 1、检查系统版本 2、点击右侧的更改适配器选项...
/proc directory in linux
Its zero-length files are neither binary nor text, yet you can examine and display themUnder Linux, everything is managed as a file; even devices are accessed as files (in the /dev directory). Although you might think that “normal” files are either text …...
周末收听:中国之声DRM短波信号实收记录
本周末(4月4日)在成都用接收了CNR中国之声的DRM短波广播,记录了13825 kHz和13810 kHz两个频率的信号数据。一个从北京发射,一个从昆明发射,传播路径和信号特征差异明显。接收条件接收地点:成都。接收时间&a…...
GPT-6曝光4月14日发布:性能暴涨40%,200万Token,AI真正进入能干活时代
4月14日,OpenAI将发布迄今最强大的AI模型多个独立消息源已确认:OpenAI下一代旗舰模型GPT-6,代号"Spud"(土豆),预计在2026年4月14日正式发布。核心数据:相比GPT-4o性能提升超40%&#…...
终极nvm-windows完整指南:Windows平台Node.js版本管理专业解决方案
终极nvm-windows完整指南:Windows平台Node.js版本管理专业解决方案 【免费下载链接】nvm-windows A node.js version management utility for Windows. Ironically written in Go. 项目地址: https://gitcode.com/gh_mirrors/nv/nvm-windows nvm-windows是Wi…...
【限时技术内参】EF Core团队内部测试报告流出:向量搜索启用后DbContext并发吞吐量下降41%的根因与热修复补丁
第一章:Entity Framework Core 10 向量搜索扩展 避坑指南Entity Framework Core 10 原生未提供向量搜索能力,需依赖第三方扩展(如 EFCore.Vector 或数据库原生支持)实现相似性检索。开发者常因忽略底层向量存储格式、索引策略或查…...
WWDC技术笔记SEO优化策略:让更多开发者发现这个宝藏资源
WWDC技术笔记SEO优化策略:让更多开发者发现这个宝藏资源 【免费下载链接】WWDC You dont have the time to watch all the WWDC session videos yourself? No problem me and many contributors extracted the gist for you 🥳 项目地址: https://git…...
Chrome-Charset扩展深度解析:编码检测与Manifest V3架构实战指南
Chrome-Charset扩展深度解析:编码检测与Manifest V3架构实战指南 【免费下载链接】Chrome-Charset An extension used to modify the page default encoding for Chromium 55 based browsers. 项目地址: https://gitcode.com/gh_mirrors/ch/Chrome-Charset C…...
3步激活旧iOS设备:Legacy iOS Kit让经典设备重获新生
3步激活旧iOS设备:Legacy iOS Kit让经典设备重获新生 【免费下载链接】Legacy-iOS-Kit An all-in-one tool to restore/downgrade, save SHSH blobs, jailbreak legacy iOS devices, and more 项目地址: https://gitcode.com/gh_mirrors/le/Legacy-iOS-Kit 当…...
为什么你的低代码表单在高并发下崩了?——基于TPS 3800+的真实压测日志,还原PHP-FPM+Redis缓存穿透链路
第一章:低代码表单的核心架构与PHP实现边界低代码表单系统并非“无代码”,而是将表单建模、渲染、校验、数据绑定与后端集成等能力抽象为可配置层,其核心架构通常由元数据驱动引擎、可视化设计器、动态渲染器、规则执行器及服务适配器五部分构…...
VINS-Mono代码架构深度解析:从feature_tracker到pose_graph,搞懂每个模块在做什么
VINS-Mono代码架构深度解析:从feature_tracker到pose_graph,搞懂每个模块在做什么 当你第一次成功运行VINS-Mono并看到实时轨迹在RVIZ中流畅呈现时,那种成就感不言而喻。但作为追求技术深度的开发者,我们不会满足于"能跑通&q…...
TypeScript 快速上手:环境配置与编译模型
1. 前言 TypeScript 在游戏开发领域的应用日益广泛,Cocos Creator、Egret、LayaAir 等引擎均将其作为主要开发语言,PuerTS 方案也让 Unity 开发者能够以 TypeScript 编写逻辑。对于具备 C# 或 C 背景的开发者而言,TypeScript 的类型系统并不…...
