目录
使用范围:只能作用在方法和构造函数之上。
@SneakyThrows注解的使用:主要是消除代码中异常处理代码。
1.异常引入
Java常见的异常有两种
1.Exception即非运行时异常(编译异常)。
2.RuntimeException即运行时异常。
Exception类异常,强制要求方法抛出可能出现的异常,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。设计的目的是为了提醒开发者处理一些场景中必然可能存在的异常情况。比如网络异常造成IOException。
2.@SneakyThrows
大部分情况下的异常,我们都是一路往外抛了事。所以渐渐的大家处理Exception的常见手段就是外面包一层RuntimeException,接着往上丢。这种解决思想尤其在Spring中到处出现。参见《Spring in Action》。
try {
} catch (Exception e) {
throw new RuntimeException(e);
}
Lombok的@SneakyThrows就是为了消除这样的模板代码。
使用注解后不需要担心Exception的处理
@SneakyThrows
public void utf8ToString(byte[] bytes) {
return new String(bytes, "UTF-8");
}
真正生成的代码(在.java文件中使用@SneakyThrows注解,.class文件会自动try-catch)
public void utf8ToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (Exception e) {
throw Lombok.sneakyThrow(e);
}
}
3.SneakyThrows注解的源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package lombok;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.SOURCE)
public @interface SneakyThrows {
Class<? extends Throwable>[] value() default {Throwable.class};
}
4.实现及原理
实现
处理异常的方法
1.throw到上层(方法体上)
2.try-catch
3.使用注解@SneakyThrows,兼容throw以及try-catch,可以对指定的异常抛出,比如@SneakyThrows(InterruptedException.class)
public class SneakyThrowDemo {
// 方法主动声明可能出现的异常
public void test1() throws IllegalAccessException, InstantiationException {
SneakyThrowDemo sneakyThrowDemo = SneakyThrowDemo.class.newInstance();
}
// 使用SneakyThrows注解
@SneakyThrows
public void test2() {
SneakyThrowDemo sneakyThrowDemo = SneakyThrowDemo.class.newInstance();
}
// test2方法的编译结果
public void test3() {
try {
SneakyThrowDemo sneakyThrowDemo = SneakyThrowDemo.class.newInstance();
} catch (Throwable e) {
// 调用Lombok方法转化为RuntimeException
throw Lombok.sneakyThrow(e);
}
}
}
原理
显然魔法 藏在Lombok.sneakyThrow(t);中。可能大家都会以为这个方法就是new RuntimeException()之类的。然而事实并非如此。阅读代码可以看出整个方法其实最核心的逻辑是throw (T)t;,利用泛型将我们传入的Throwable强转为RuntimeException。虽然事实上我们不是RuntimeException。但是没关系。因为JVM并不关心这个。泛型最后存储为字节码时并没有泛型的信息。这样写只是为了骗过javac编译器。源码中注释有解释。
public static RuntimeException sneakyThrow(Throwable t) {
if (t == null) throw new NullPointerException("t");
return Lombok.<RuntimeException>sneakyThrow0(t);
}
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
throw (T)t;
}