[Guava官方文档翻译] 6. 用Guava辅助Throwable异常处理 (Throwables Explained)
我的技术博客经常被流氓网站恶意爬取转载。请移步原文:http://www.cnblogs.com/hamhog/p/3537508.html ,享受整齐的排版、有效的链接、正确的代码缩进、更好的阅读体验。
Guava的 Throwables 工具常常可以让exception处理更方便。
Propagation
有时候,你会想把捕获的exception抛到上一个try/catch块。对于 RuntimeException 和 Error 尤为如此,它们不需要 try/catch 块,但可能被其他的 try/catch 块无意捕获。
Guava 提供了一些工具来简化propagate exception。例如:
try{
someMethodThatCouldThrowAnything();
}catch(IKnowWhatToDoWithThisException e){
handle(e);
}catch(Throwable t){
Throwables.propagateIfInstanceOf(t, IOException.class);
Throwables.propagateIfInstanceOf(t, SQLException.class);
throw Throwables.propagate(t);
}
这里的每个方法都会抛出它们自己的exception,但是throw最终结果 —— 如,throw Throwables.propagate(t) —— 对编译检查有指示作用,提示这里肯定会抛出一个exception。
这里是Guava提供的propagation方法的简单总结:
| 参数形式 | 解释 |
| RuntimeException propagate(Throwable) | 如果参数throwable是 RuntimeException 或 Error 则原样propagate,否则将它包入 RuntimeException 中抛出。保证抛出。返回值是 RuntimeException 类型,因此你可以像上面那样写 throw Throwables.propagate(t) ,这样Java编译器会明白这一行肯定会抛出一个exception。 |
| void propagateIfInstanceOf(Throwable, Class<X extends Exception>) throws X | 仅当参数throwable是 X 类型时,原样propagate。 |
| void propagateIfPossible(Throwable) | 仅当参数throwable是 RuntimeException 或 Error 类型时,原样propagate。 |
| void propagateIfPossible(Throwable, Class<X extends Throwable>) throws X | 仅当参数 throwable 是 RuntimeException 或 Error 或 X 类型时,原样propagate。 |
Throwables.propagate的用途
模仿Java 7的多重catch和重新throw
一般来说,调用者如果想要让exception沿着调用栈传播,他只要不写 catch 块就可以了。既然他不打算在exception后恢复,他恐怕也不需要写入log或者采取什么其他行动。他可能需要进行一些清理工作,但是无论有没有expction都会需要,因此清理工作会放在 finally 块中。尽管如此,会重新throw的 catch 块有时还是有意义的:也许调用者想要在传播exception之前先更新崩溃计数器,或者他只想在特定条件下传播exception。
只有一种exception的时候,catch和重新throw是简单直接的。麻烦的是有多种exception的时候:
@Overridepublicvoid run(){
try{
delegate.run();
}catch(RuntimeException e){
failures.increment();
throw e;
}catch(Error e){
failures.increment();
throw e;
}
}
Java 7用 multicatch 来解决这个问题:
}catch(RuntimeException|Error e){
failures.increment();
throw e;
}
但不用Java 7的用户就没办法了。他们也想写如下的代码,但编译器不允许抛出Throwable类型的变量:
}catch(Throwable t){
failures.increment();
throw t;
}
解决方案是用 throw Throwables.propagate(t) 来替换 throw t 。仅就这种情况而言, Throwables.propagate 跟之前代码的功能完全相同。但是,代码里写 Throwables.propagate 很容易有一种隐藏的副作用。具体来说,要注意上面这种模式只适用于 RuntimeException 和 Error。如果 catch 块可能捕捉checked exception,你还需要调用 propagateIfInstanceOf 来保证正常功能,因为 Throwables.propagate 无法直接传播checked exception。
总体来说,这种 propagate 的用法效果一般。在Java 7下没必要这样做。在其他版本的Java下,这样能略微减少重复,但是一个简单的Extract Method重构也可以达到同样效果。另外,使用 propagate makes it easy to accidentally wrap checked exceptions.
无用功:把 throws Throwable 转化为 throws Exception
某些API,尤其是Java reflection API 和 (相应的) JUnit,有抛出 Throwable 的方法。运用这些API可能很麻烦,因为就算是最通用的API一般也只声明throws Exception。 Throwables.propagate 是为非 Exception ,非 Error 的 Throwable 准备的。这个例子声明了一个执行JUnit测试的 Callable :
public void call() throws Exception{
try{
FooTest.super.runTest();
}catch(Throwable t){
Throwables.propagateIfPossible(t,Exception.class);
Throwables.propagate(t);
}
return null;
}
这里没必要propagate(),并且第二行与="throw new RuntimeException(t)"等价。 (顺便说一句:这个例子也提醒了我 propagateIfPossible 可能令人迷惑,因为它不仅传播参数指定的类型,也传播 Error 和 RuntimeException。)
这种模式 (或者类似的写法,如"throw new RuntimeException(t)") 在Google的代码库里至少出现 30 次。(搜索'propagateIfPossible[^;]* Exception.class[)];'试试。) 采用"throw new RuntimeException(t)"写法的略占多数。我们也有可能想要一个"throwWrappingWeirdThrowable"方法来做Throwable到Exception的转换,但是既然两行就能搞定,这个方法还是没有太大必要,除非我们要废除propagateIfPossible方法。
Throwables.propagate有争议的用法
有争议:把 checked exception 转化为 unchecked exception
理论上,unchecked exception表示有bug,checked exceptions表示在你控制范围之外的问题。但在实践上,即使JDK有时也会用错 (至少,对于某些方法,没有普遍认同的正确答案)。
因此,有时调用者需要让这两种exception类型相互转化:
try{
return Integer.parseInt(userInput);
}catch(NumberFormatException e){
throw new InvalidInputException(e);
}
try{
return publicInterfaceMethod.invoke();
}catch(IllegalAccessException e){
throw new AssertionError(e);
}
有时候,这些调用者会用 Throwables.propagate 。有什么坏处呢?最主要的问题是代码的含义不太明显。throw Throwables.propagate(ioException) 有什么效果?throw new RuntimeException(ioException) 有什么效果?这两行代码功能是相同的,但后者更直白。前者使人生疑:"这是在做什么?应该不只是打包成 RuntimeException 吧?如果只为这个,为何不写一个wrapper方法呢?"只能承认,部分问题在于"propagate"这个名字很含糊。(它是一个抛出未声明exception的方式吗?) 也许换成"wrapIfChecked"会好一些。但即使叫这个名字,在已知checked exception上调用它也没有什么优势。可能还会有副作用: 也许会有比普通 RuntimeException 更合适的抛出类型 -- 比如, IllegalArgumentException。
我们有时也会看到在exception仅仅有可能是checked exception时用 propagate 。相对来说,这种做法后果小一些,也不那么直白:
}catch(RuntimeException e){
throw e;
}catch(Exception e){
throw new RuntimeException(e);
}
}catch(Exception e){
throw Throwables.propagate(e);
}
尽管如此,这里不可忽视的问题在于将checked exception转化为unchecked exception的行为本身。在某些情况下是无可厚非的,但更多的时候这样做是在逃避对常规 checked exception 的处理。这让我们思考checked exception本身是否就是一个坏主意。我不想说得这么深入。姑且说 ,Throwables.propagate 不是让 Java 使用者用来忽略 IOException 及类似异常的。
有争议: Exception隧道
但是当你实现一个不允许throw exception的方法时怎么办呢?有时你需要把exception打包在unchecked exception中。这样没问题,但同样的,对于单纯的打包 propagate 没有必要。事实上,自己实现打包会更好一些:如果你把每个exception(而不只是checked exception)打包,那就可以在另一端解包,减少特殊处理。另外,最好打包成自定义的exception类型。
有争议: 重新抛出其他线程的exception
try{
return future.get();
}catch(ExecutionException e){
throw Throwables.propagate(e.getCause());
}
这里有几件要考虑的事:
- 对于checked exception:参见上面的"把 checked exception 转化为 unchecked exception"一节。但如果此处已知不会抛出checked exception呢? (也许是 Runnable 的结果。) 如上所述,你可以catch这个exception然后抛出 AssertionError ;propagate 没有什么用。特别的是,对于Future,也可以考虑Futures.get。
- 对于非Exception,非Error的Throwable。(好吧,这个可能性不大,但是如果你试图直接重新throw它,编译器会强迫你考虑这种可能) 参考上面的 "把 throws Throwable 转化为 throws Exception" 一节.
- 对于unchecked exception或error。那么它会直接被重新throw。不幸的是,它的stack trace会显示原先产生exception的线程,而不是当前传播到的线程。一般最好的结果是把两个线程的stack trace都包括在exception chain中,就像 get 抛出的 ExecutionException 一样。(这其实不是 propagate 的问题; 这是所有把 exception 在另一个线程抛出的共同问题。)
因果链
Guava让研究一个exception的因果链(causal chain)更简单了一些,它提供了以下3个很有用的方法,功能顾名思义:
| Throwable getRootCause(Throwable) |
| List<Throwable> getCausalChain(Throwable) |
| String getStackTraceAsString(Throwable) |
中文翻译自Guava官方文档:GuavaExplained - PreconditionsExplained 译者:戴仓薯
[Guava官方文档翻译] 6. 用Guava辅助Throwable异常处理 (Throwables Explained)的更多相关文章
- [Guava官方文档翻译] 4. 使用Guava Ordering排序 (Ordering Explained)
本文地址:http://www.cnblogs.com/hamhog/p/3537233.html 示例 assertTrue(byLengthOrdering.reverse().isOrdered ...
- [Guava官方文档翻译] 7. Guava的Immutable Collection(不可变集合)工具 (Immutable Collections Explained)
我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3538666.html ,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体 ...
- [Guava官方文档翻译] 2.使用和避免使用null (Using And Avoiding Null Explained)
本文地址:http://www.cnblogs.com/hamhog/p/3536647.html "null很恶心." -Doug Lea "这是一个令我追悔莫及的错误 ...
- 用Guava辅助Throwable异常处理
Guava的 Throwables 工具常常可以让exception处理更方便. Propagation 有时候,你会想把捕获的exception抛到上一个try/catch块.对于 RuntimeE ...
- [Guava官方文档翻译] 5. Guava的Object公共方法 (Common Object Utilities Explained)
我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3537367.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...
- [Guava官方文档翻译] 3. 前置条件检查(Preconditions Explained)
本文地址:http://www.cnblogs.com/hamhog/p/3536964.html 前置条件检查 Guava提供了一些检查前置条件的utilities.我们强烈建议静态import这些 ...
- [Guava官方文档翻译] 1.Guava简介 (Introduction)
用户指南 Guava包含Google在Java项目中用到的一些核心库:collections, caching, primitives support, concurrency 库, common a ...
- [转]Google Guava官方教程(中文版)
Google Guava官方教程(中文版) http://ifeve.com/google-guava/
- Google Guava官方教程(中文版)
Google Guava官方教程(中文版) 原文链接 译文链接 译者: 沈义扬,罗立树,何一昕,武祖 校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库, ...
随机推荐
- android studio简易了解第一部分
1.如果还没下载 jdk,先把jdk下载,然后下载android studio 安装 百度 android studio ,百度软件中心可以下载. Android官网可以下载: 网址: htt ...
- 【19】设计class犹如设计type
设计class 的时候,需要好好考虑下面的问题: 1.新type的对象应该如何被创建和销毁? 2.对象的初始化和对象的赋值该有什么样的差别? 3.新type的对象如果pass by value,意味着 ...
- Nginx加多个tomcat实现负载均衡,动静分离
一:Nginx+Tomcat的动静分离 所谓动静分离就是通过nginx(或apache等)来处理用户端请求的图片.html等静态的文件,tomcat(或weblogic)处理jsp.do等动态文件,从 ...
- android Animation 动画效果介绍
Android的animation由四种类型组成 XML中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面转移旋转动 ...
- uboot中gd的定义和使用
近期在做uboot中nand启动相关的工作,遇到一个问题一直纠结着.如今最终明确了这个问题,想想还有好多兄弟在某个黑暗的角落里或者某台电脑前纠结着呢,所以赶紧写下来以供查阅. uboot versio ...
- 定时自动同步文件,支持多文件夹同步,支持过滤文件和文件夹,解决FileSystemWatcher多次文件触发事件(源码)
博客园里面有很多同步工具和软件,关于FileSystemWatcher类解释的也很多,但收集了很多文章后,感觉没好的方法,自己没事写了一个定时文件同步,借鉴了很多博客园朋友的东西: 上主菜: 配置文件 ...
- linux vi 撤销重做于前进后退--转
在vi中按u可以撤销一次操作 u 撤销上一步的操作Ctrl+r 恢复上一步被撤销的操作 注意:如果你输入“u”两次,你的文本恢复原样,那应该是你的Vim被配置在Vi兼容模式了.重做如果你撤销得太多 ...
- C#开发-ftp操作方法整理
1.整理简化了下C#的ftp操作,方便使用 1.支持创建多级目录 2.批量删除 3.整个目录上传 4.整个目录删除 5.整个目录下载 2.调用方法展示, var ftp ...
- ubuntu 13.04 root权限设置方法详解
很多朋友安装升级Ubuntu 13.04之后不知道ubuntu 13.04 root权限设置的具体方法,今天这篇文章就将为大家详细介绍设置root权限的步骤,新手朋友可以来看一看哦~ Ubunto 1 ...
- web前端开发前景怎么样?
对于web前端开发,对现今前端的发展,中国的发展还很落后,中国没有Jquery,没有Node.js,其中最主要的一点是,中国的前端比较封锁,大家都没有分享的觉悟.回头看看,那些发展比较快的行业.软件, ...