Java: 帮助Checked Exception通过Unchecked Exception通道的技术
最近用到了Java的异步框架,名叫Future。它通常是与λ表达式结合来使用的。λ表达式是通过某类注解(接口)定义 的。但是看了类似java.lang.Runnable.run()这些方法都没有定义throws,也就是意味着它不支持抛出受检异常(checked exception)。
我采取了一个方案,自己捕获受检异常,并把它保存到一个集合中。在各个异步任务都结束的时候检查这个集合,如果非空则进行回滚操作。这个方案的缺点是,与异步框架结合的不够好。有受检异常发生时,不会自动取消后续的异步任务。
能不能把所有的受检异常都变成非受检异常(unchecked exception)呢?就是说,在一个受检异常发生时,第一时间就把它变成非受检异常。从而实现整个系统中的受检异常在尽可能小的范围内发生和传播。或许JDK也希望我们这么做,不然为什么这些与Future紧密相关的λ表达式都不支持受检异常呢。鉴于现有的代码以及大量的组件都使用了受检异常。如果都转为非受检异常,势必要改造很多个类,既有工作量也有挑战,暂且不能这么干。
能不能做一个隧道,帮助受检异常顺利的通过不支持受检异常的通道,并在通过后对其进行恢复。示意图如下。
经过实验,这个方案是可行的。我封装了一个类来做这件事,代码如下。
public class CheckedExceptionWrapperException extends RuntimeException { public CheckedExceptionWrapperException(String message, Throwable ex) { super(message, ex); } public static CheckedExceptionWrapperException wrap(String message, Excepton ex) { if (ex isinstanceof CheckedExceptionWrapperException) { return (CheckedExceptionWrapperException) ex; } return new CheckedExceptionWrapperException(message, ex); } public static Excepton unwrap(Excepton ex) { if (ex isinstanceof CheckedExceptionWrapperException) { return ((CheckedExceptionWrapperException) ex).unwrap(); } return ex; } public Excepton unwrap() { return this.getInnerExeption(); } }
用法举例
import java.util.concurrent.CompletableFuture; task1 = CompletableFuture.runAsyn(() -> { try { doSomeThing1(); } catch(Excepton ex) { throw CheckedExceptionWrapperException.wrap(ex); } }); task2 = task1.thenRun(() -> { try { doSomeThing2(); } catch(Excepton ex) { throw CheckedExceptionWrapperException.wrap(ex); } }); task3 = CompletableFuture.runAsyn(() -> { try { doSomeThing3(); } catch(Excepton ex) { throw CheckedExceptionWrapperException.wrap(ex); } }); task4 = task3.thenRun(() -> { try { doSomeThing4(); } catch(Excepton ex) { throw CheckedExceptionWrapperException.wrap(ex); } }); try { CompletableFuture.all(task2, task4).get(); } catch(Excepton ex) { throw CheckedExceptionWrapperException.unwrap(ex); }
达到的效果就是无论发生受检异常还是非受检异常,都能够利用异步框架的机制,取消后续任务的执行。能够利用异步框架进行传播,并在需要的时候进行恢复(抛出与捕获),恢复后能得到原始的调用栈。
实际使用时发现异步框架在捕获到异常时,还会再包装一层它自己的异常,所以我也在这个类中对这一层进行解包装。代码如下。
public class CheckedExceptionWrapperException extends RuntimeException { // ... public static Excepton unwrap(Excepton ex) { if (ex isinstanceof java.util.concurrent.CompletionException) { return unwrap(ex.getInnerExeption()) } if (ex isinstanceof CheckedExceptionWrapperException) { return ((CheckedExceptionWrapperException) ex).unwrap(); } return ex; } // ... }
我把这项技术称作为异常隧道。这个隧道帮助受检异常通过了原本通不过的通道。有了这个异常隧道,终于可以好好地使用Future了。
博主简介:佘焕敏(shé),洋名 Billy Sir。
关注编程基础技术,并致力于研究软件的自动化生成。 对编程规范化、面向对象的极致使用也有着浓厚的兴趣。 同时非常希望能够写程序到65岁。
只有工匠精神,才能把常人觉得单调乏味的代码,当作作品雕刻成艺术品。
重点:这里几乎每一篇文章,都是我认真创作的,凝结了心血。写作从来都不是一件容易的事,对从小语文不好的我而言,是难上加难。而且,你发现没有,文中没有广告。这篇文章介绍了为什么要写这些文章。
![微信赞赏码](https://img2022.cnblogs.com/blog/33556/202211/33556-20221101085333455-1717538163.png)