Lombok的@SneakyThrows詳解
一、簡介
在java
的異常體系中Exception
異常有兩個分支,一個是運行時異常RuntimeException
,一個是編譯時異常,在Exception
下的所有非RuntimeException
異常,比如IOException
、SQLException
等;所有的運行時異常不捕獲,編譯時異常是一定要捕獲,否則編譯會報錯。@SneakyThrows
就是利用了這一機制,將當前方法拋出的異常,包裝成RuntimeException
,騙過編譯器,使得調用點可以不用顯示處理異常信息。
二、原理
/*
* 若不使用@SneakyThrows注解,newInsstance方法會要求拋出InstantiationException,
* IllegalAccessException異常,且調用sneakyThrowsTest()的地方需要捕獲這些異常,
* 加上@SneakyThrows注解之后就不需要捕獲異常信息。
*/
@SneakyThrows
private void sneakyThrowsTest(){
SneakyThrowsDemo.class.newInstance();
}
如下為反編譯之后的結果
private void sneakyThrowsTest() {
try {
HelloController.class.newInstance();
} catch (Throwable e) {
// 調用Lombok方法轉化為RuntimeException
throw Lombok.sneakyThrow(e);
}
}
// =========== ombok =========
public static RuntimeException sneakyThrow(Throwable t) {
if (t == null) {
throw new NullPointerException("t");
} else {
return Lombok.<RuntimeException>sneakyThrow0(t);
}
}
/*
* 這個方法是關鍵,這里對入參類型的約束為<T extends Throwable>,將異常強轉為T類型
*/
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
throw (T)t;
}
那么問題來了,為什么這個地方可以對原來的異常進行強轉為RuntimeExcption
?以下為直接強轉的代碼,顯然運行之后報類型轉換異常。
private void sneakyThrowsTest() {
try {
throw new Exception();
} catch (Throwable e) {
// 直接將e強轉為RuntimeException,運行到這里會報類型轉換異常。
throw (RuntimeException)e;
}
}
實際上,這種做法是一種通過泛型欺騙了編譯器,讓編譯器在編譯期不報錯,而最后在JVM
虛擬機中執行的字節碼的並沒有區別編譯時異常和運行時異常,只有是不是和拋不拋異常而已。