Lombok的@SneakyThrows详解
[TOC]
一、简介
在java的异常体系中Exception异常有两个分支,一个是运行时异常RuntimeException,一个是编译时异常,在Exception下的所有非RuntimeException异常,比如IOException、SQLException等;所有的运行时异常不捕获,编译时异常是一定要捕获,否则编译会报错。@SneakyThrows就是利用了这一机制,将当前方法抛出的异常,包装成RuntimeException,骗过编译器,使得调用点可以不用显示处理异常信息。
二、原理
1 2 3 4 5 6 7 8 9
|
@SneakyThrows private void sneakyThrowsTest(){ SneakyThrowsDemo.class.newInstance(); }
|
如下为反编译之后的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| private void sneakyThrowsTest() { try { HelloController.class.newInstance(); } catch (Throwable e) { throw Lombok.sneakyThrow(e); } }
public static RuntimeException sneakyThrow(Throwable t) { if (t == null) { throw new NullPointerException("t"); } else { return Lombok.<RuntimeException>sneakyThrow0(t); } }
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T { throw (T)t; }
|
那么问题来了,为什么这个地方可以对原来的异常进行强转为RuntimeExcption?以下为直接强转的代码,显然运行之后报类型转换异常。
1 2 3 4 5 6 7 8
| private void sneakyThrowsTest() { try { throw new Exception(); } catch (Throwable e) { throw (RuntimeException)e; } }
|
实际上,这种做法是一种通过泛型欺骗了编译器,让编译器在编译期不报错(报警告),而最后在JVM虚拟机中执行的字节码的并没有区别编译时异常和运行时异常,只有是不是和抛不抛异常而已。