【双语】不为人知的集合异常检查工具:Collections.checkedCollection()

本贴最后更新于 2513 天前,其中的信息可能已经东海扬尘

英文原文链接:https://www.javaspecialists.eu/archive/Issue251.htm

摘要: Java 在 5.0 的时候已经具备了检查集合中的元素类型是否正确的能力,但是只有很少一部分程序员知道这个功能,这个功能在代码调试阶段起到了非常巨大的作用,它可以在早期就把异常抛出。

Dinosaurs roamed the earth. Fred Flintstone wrote Applets with JBuilder. A shaft of lightning split the dark sky and with a flash <> appeared on his amber screen. Fred scratched his head. _"What on flat earth was List<String>?"

在那古老的恐龙时代,原始人弗莱德正在用 Jbuilder 写一个 Applets 程序,一道闪电劈开了昏暗的天空,<> 在弗莱德那琥珀显示器上不停的闪烁。弗莱德抓了抓脑袋说道:“List<String> 是个什么鬼?”

It was the advent of generics.

这就是泛型的降临

Programmers do not like change. Or rather, we do not like change that will break something that was working perfectly well before. Java 1.4 introduced the **assert** keyword. It broke a bunch of our classes. Java 1.5 added **enum** to the relatively short list of reserved words, a popular name for Enumeration instances. And that was the last time a new keyword was added to the Java Programming Language. Sun Microsystems' engineers knew their audience. They also knew that for their generics to be accepted, old code should preferably still compile without any changes necessary.

程序员不喜欢变化,不如说我们不喜欢去接受一些新的改变我们编程方式的东西,因为我们之前的编程方式已经做的很熟练和完美了。Java1.4 加入了“断言” assert 关键字,他改变了我们大量的 java 类,Java1.5 添加了枚举 enum (Enumeration instances)这个流行的关键字,这也是最后一次向 Java 中添加关键字了。Sun 公司(java 的创始公司已被 Oracle 收购)的工程师了解他们的用户,并很肯定他们的用户也能接受泛型,因为使用泛型你不需要修改之前的任何代码。

Generics were designed so we could ignore them if we wanted to. The javac compiler would issue a faint sigh, but would still compile everything as before. How did they do it? Type erasure was the magic ingredient. When they compiled the class ArrayList, the generic type parameter was erased and replaced with Object. Even the E[] was erased to Object[]. In Java 6, they changed the element array in ArrayList to the more honest Object[].

泛型虽然被加入到了 Java 体系总但我们完全可以不去使用它,编译器只是会有一个警告标志,但不影响程序的执行。他们怎么做到的?这就是类型擦除(Java 泛型的内部机制,有兴趣的朋友可以百度一下),当我们编译 ArrayList 这个类时,泛型类型是被擦除掉的,被 Object(对象的祖宗)这个类型取代,甚至异常占位符 E[]也被替换成了 Object[] (百度一下泛型占位符的基本概念),在 Java 1.6 版本,他们把 ArrayList 里装载数据的类型都改变成了 Object[]。

1.  private transient Object[] elementData;

有兴趣的可以去百度一下这一行代码,深入的理解一下 ArrayList 这个使用非常频繁的集合。
(这部分看不明白不影响后面的阅读)

By not distinguishing at runtime between ArrayList<String> and ArrayList<Integer>, we allowed Java programmers to still shoot themselves in the foot like so:

如果不去关心 ArrayList<String> 和 ArrayList<Integer> 会出现的问题,我们依然允许程序员去拿石头砸自己的脚,如下面的代码:

import java.util.*;  
public class  FootShootJava5{  
  public  static  void main(String... args)  {  
	 List<String> names =  new  ArrayList<>();
	 Collections.addAll(names,  "John",  "Anton",  "Heinz");
	 List huh = names;  
	 List<Integer> numbers = huh; 
	 numbers.add(42);
  }  
}  

Sure, javac would emit a warning, but at runtime everything would appear to work. It was only when we retrieved elements from ArrayList that a cast to String was inserted into the client code and then a ClassCastException would jump in our faces. This is an example of an exception that is thrown late. A while after the incorrect object has been inserted into the ArrayList, we discover that it wasn't a String after all, thus if we add the following we see the problem:

这段代码并不会报错,一切运行正常,但是当我们从集合中取出数据并加以操作的时候,异常就会跳到我们脸上,这种异常是一种_后期异常(暂时这么翻译,在本例意思是异常并没有出现在不同类型数据插入时,而是在使用时才出现)_, 下面的例子中解释了这一点:

import java.util.*;  
import  static java.util.stream.Collectors.*;
public  class  FootShootJava8  {  
  public  static  void main(String... args)  {  
	List<String> names =  new  ArrayList<String>();
	Collections.addAll(names,  "John",  "Anton",  "Heinz");
	List huh = names;
	List<Integer> numbers = huh;
	//应该出现异常的位置,因为插入了非String类型的数据,但这一行并没有抛出异常,而是在下一行抛出了
	numbers.add(42);  
	//stream:jdk1.8新特性,可百度一下StreamAPI,下面这行代码意思是将集合中的所有元素连接在一起,以+号分隔开
	//这一行抛出了类型转换异常
	System.out.println(names.stream().collect(joining("+")));  
	}
}  

(StreamAPI 相关博文:http://blog.csdn.net/u010425776/article/details/52346644)

esults in a rather grumpy:

结果让人很暴躁

ClassCastException:  Integer cannot be cast to CharSequence 
	at ReduceOps$3ReducingSink.accept()
	at ArrayList$ArrayListSpliterator.forEachRemaining()
	at AbstractPipeline.copyInto()
	at AbstractPipeline.wrapAndCopyInto()
	at ReduceOps$ReduceOp.evaluateSequential()
	at AbstractPipeline.evaluate() 
	at ReferencePipeline.collect() 
	at FootShootJava8.main 

Since the exception is thrown late, it results in wasted programmer effort searching for where the wrong type could have been inserted into the list.

异常在运行时抛出,而且抛出的位置让程序员很难找到问题所在,浪费了大量的时间。

And yet there has always been a better way, even in Java 5. We can wrap our List object with a checkedList. This way, every time we add an element, it is checked that it is of the correct type. The ClassCastException thus happens during the add(42), rather than much later. For example:

但是总有办法来解决这个问题,甚至在 java1.5 我们也能进行集合的检查,有种方法可以在数据插入集合时就可以去检查数据类型是否正确,在 add(42) 这一行执行时就可以抛出异常不就是我们想要的?看下面的代码:

import java.util.*;
import  static java.util.stream.Collectors.*;
public  class  FootShootWithSafetyCatch  {  
	public  static  void main(String... args)  {  
		List<String> names =  Collections.checkedList(new  ArrayList<String>(),  String.class);
		Collections.addAll(names, "John","Anton","Heinz");
		List huh = names;
		List<Integer> numbers = huh;
		numbers.add(42); //异常将会出现在这一行
		System.out.println(names.stream().collect(joining("+")));
	}
}  

We would still get a ClassCastException, but at the place where the damage was done:

我们依然会触发一个类型转换异常,但是这次抛出的位置正是错误出现的位置。

ClassCastException:  Attempt to insert class  Integer element into collection with element type class  String 
	at java.util.Collections$CheckedCollection.typeCheck() 
	at java.util.Collections$CheckedCollection.add() 
	at FootShootWithSafetyCatch.main 

The checked collection would also discover objects that are added via reflection and throw a ClassCastException. It could not safeguard against "deep reflection", but then not much can.

这个集合检查功能同样可以用于反射检查,抛出类型转换异常,有兴趣的可以去查阅资料,但不能去应付“深度反射”(反射层数较多),但这并不是什么大问题。

You might wonder why I am writing about a method that was added in Java 5? The reason is that hardly anybody I speak to has heard of Collections.checkedCollection() and its derivatives. It is useful to make your collections just a bit more robust against accidental or deliberate tomfoolery.

你肯定问为什么我要拿 java 1.5 来讲这个例子,原因是我想强调一下很少有人知道 Collections.checkedCollection() 和他的衍生物,这是在工作中能让我们的代码更加健壮且避免发生一些愚蠢的问题和浪费不必要的时间。

It can also be a quick and easy way to debug any ClassCastException you might discover in your system. Wrap the collection in a checked exception and the guilty party will quickly come to the fore.

这也是一种快速的方式帮助我们找到类型转换异常的方式,把集合包装起来好让问题尽快的暴露出来。

Oh one last thing, completely unrelated to Java, but definitely related to our profession. Today also marks one year since I started my running streak, running at least one mile a day, in snow, rain, lightning and 48C heat. It's been fun and a great way to think about all sorts of things. I've had far more energy, have slept better and have produced more this year than in many previous years. If you're a couch potato, I can only recommend you try Streak Running and join me in the list of people who've run for at least one year, every day. No excuses.

oh. 还有一件很重要的事,跟 Java 没关系,但是对我们的职业很有好处,今天已经是我连续跑步一年的日子,每天至少跑一公里,不管是刮风下雪,闪电还是 48 度的桑拿天,对我们做事还是工作都非常有好处,我觉得我比之前更有能量了,吃的好睡的好,工作状态比前几年要好太多了,如果你是一个懒癌患者(couch potato?)我非常的建议你跟我一样加入跑步的行列,坚持一年你将看到非常显著的效果。

Kind regards from Crete

Heinz

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Swagger

    Swagger 是一款非常流行的 API 开发工具,它遵循 OpenAPI Specification(这是一种通用的、和编程语言无关的 API 描述规范)。Swagger 贯穿整个 API 生命周期,如 API 的设计、编写文档、测试和部署。

    26 引用 • 35 回帖 • 5 关注
  • SQLServer

    SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。

    21 引用 • 31 回帖 • 4 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 361 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    5 引用 • 107 回帖
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 17 关注
  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖 • 3 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 518 关注
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    117 引用 • 99 回帖 • 209 关注
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    93 引用 • 899 回帖 • 1 关注
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    5 引用 • 15 回帖 • 101 关注
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    51 引用 • 25 回帖
  • Sillot

    Insights(注意当前设置 master 为默认分支)

    汐洛彖夲肜矩阵(Sillot T☳Converbenk Matrix),致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点。其中汐洛绞架(Sillot-Gibbet)基于自思源笔记(siyuan-note),前身是思源笔记汐洛版(更早是思源笔记汐洛分支),是智慧新录乄终端(多端融合,移动端优先)。

    主仓库地址:Hi-Windom/Sillot

    文档地址:sillot.db.sc.cn

    注意事项:

    1. ⚠️ 汐洛仍在早期开发阶段,尚不稳定
    2. ⚠️ 汐洛并非面向普通用户设计,使用前请了解风险
    3. ⚠️ 汐洛绞架基于思源笔记,开发者尽最大努力与思源笔记保持兼容,但无法实现 100% 兼容
    29 引用 • 25 回帖 • 86 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 4 关注
  • Eclipse

    Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。

    75 引用 • 258 回帖 • 624 关注
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    91 引用 • 751 回帖 • 1 关注
  • Oracle

    Oracle(甲骨文)公司,全称甲骨文股份有限公司(甲骨文软件系统有限公司),是全球最大的企业级软件公司,总部位于美国加利福尼亚州的红木滩。1989 年正式进入中国市场。2013 年,甲骨文已超越 IBM,成为继 Microsoft 后全球第二大软件公司。

    105 引用 • 127 回帖 • 370 关注
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    83 引用 • 37 回帖
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 363 关注
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 65 回帖 • 446 关注
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    354 引用 • 1823 回帖 • 1 关注
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖
  • PWL

    组织简介

    用爱发电 (Programming With Love) 是一个以开源精神为核心的民间开源爱好者技术组织,“用爱发电”象征开源与贡献精神,加入组织,代表你将遵守组织的“个人开源爱好者”的各项条款。申请加入:用爱发电组织邀请帖
    用爱发电组织官网:https://programmingwithlove.stackoverflow.wiki/

    用爱发电组织的核心驱动力:

    • 遵守开源守则,体现开源&贡献精神:以分享为目的,拒绝非法牟利。
    • 自我保护:使用适当的 License 保护自己的原创作品。
    • 尊重他人:不以各种理由、各种漏洞进行未经允许的抄袭、散播、洩露;以礼相待,尊重所有对社区做出贡献的开发者;通过他人的分享习得知识,要留下足迹,表示感谢。
    • 热爱编程、热爱学习:加入组织,热爱编程是首当其要的。我们欢迎热爱讨论、分享、提问的朋友,也同样欢迎默默成就的朋友。
    • 倾听:正确并恳切对待、处理问题与建议,及时修复开源项目的 Bug ,及时与反馈者沟通。不抬杠、不无视、不辱骂。
    • 平视:不诋毁、轻视、嘲讽其他开发者,主动提出建议、施以帮助,以和谐为本。只要他人肯努力,你也可能会被昔日小看的人所超越,所以请保持谦虚。
    • 乐观且活跃:你的努力决定了你的高度。不要放弃,多年后回头俯瞰,才会发现自己已经成就往日所仰望的水平。积极地将项目开源,帮助他人学习、改进,自己也会获得相应的提升、成就与成就感。
    1 引用 • 487 回帖 • 2 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖 • 2 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 27 关注
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    90 引用 • 562 回帖 • 1 关注
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖 • 1 关注