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

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

英文原文链接: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 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • 游戏

    沉迷游戏伤身,强撸灰飞烟灭。

    176 引用 • 815 回帖
  • Elasticsearch

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

    117 引用 • 99 回帖 • 212 关注
  • Firefox

    Mozilla Firefox 中文俗称“火狐”(正式缩写为 Fx 或 fx,非正式缩写为 FF),是一个开源的网页浏览器,使用 Gecko 排版引擎,支持多种操作系统,如 Windows、OSX 及 Linux 等。

    8 引用 • 30 回帖 • 408 关注
  • 开源中国

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

    7 引用 • 86 回帖 • 1 关注
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 99 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 51 关注
  • JetBrains

    JetBrains 是一家捷克的软件开发公司,该公司位于捷克的布拉格,并在俄国的圣彼得堡及美国麻州波士顿都设有办公室,该公司最为人所熟知的产品是 Java 编程语言开发撰写时所用的集成开发环境:IntelliJ IDEA

    18 引用 • 54 回帖
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    12 引用 • 54 回帖 • 63 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    15 引用 • 122 回帖
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖
  • 工具

    子曰:“工欲善其事,必先利其器。”

    286 引用 • 729 回帖
  • JWT

    JWT(JSON Web Token)是一种用于双方之间传递信息的简洁的、安全的表述性声明规范。JWT 作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 的形式安全的传递信息。

    20 引用 • 15 回帖 • 3 关注
  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 210 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 48 关注
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    944 引用 • 943 回帖
  • DevOps

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

    47 引用 • 25 回帖 • 1 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 762 关注
  • 机器学习

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

    83 引用 • 37 回帖 • 1 关注
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    524 引用 • 4601 回帖 • 700 关注
  • flomo

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

    5 引用 • 107 回帖
  • Netty

    Netty 是一个基于 NIO 的客户端-服务器编程框架,使用 Netty 可以让你快速、简单地开发出一个可维护、高性能的网络应用,例如实现了某种协议的客户、服务端应用。

    49 引用 • 33 回帖 • 21 关注
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 40 关注
  • HBase

    HBase 是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文 “Bigtable:一个结构化数据的分布式存储系统”。就像 Bigtable 利用了 Google 文件系统所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力。

    17 引用 • 6 回帖 • 73 关注
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    12 引用 • 54 回帖 • 164 关注
  • 小薇

    小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。

    由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!

    34 引用 • 467 回帖 • 742 关注