Lombokの@SneakyThrows(lombok.SneakyThrows)アノテーションの利用サンプルです
https://projectlombok.org/features/SneakyThrows.html
Javaの例外は、チェック例外と実行時例外の2つに大別されます。
チェック例外が発生する場面では、try-catchで処理するかthrows節に記述して上位へスローするかの対処が必要となります。
このチェック例外に関して、場合によっては発生しないことが確定しているにも関わらず対処しなければならないような場面があります。
このような場面で、@SneakyThrows
を使用すると対象の例外の発生を覆い隠すことができます(実際には実行時例外にラップします)。
ただし、@SneakyThrows
は他のアノテーションに比べて使いどころが難しいように感じます。
CloneNotSupportedExceptionを@SneakyThrowsで隠す
CloneNotSupportedExceptionはCloneable
を実装していないクラスでObjectクラスのcloneメソッドを呼び出した場合にスローされます。
ただし、Cloneable
実装していてもCloneNotSupportedException
がチェック例外であるため何らか処理するコードを書かなければなりません。
以下に@SneakyThrows
を使わなかった場合と使った場合のコード例を記載しています。
■CloneNotSupportedExceptionをスローする場合
CloneNotSupportedException
を上位にスローする戦略を取った場合のコード例です
cloneメソッドがCloneNotSupportedException
をスローするようになっていますが、Cloneable
インターフェースを実装しているので発生することがありません。
それにもかかわらず、DefaultClone#cloneを呼び出す側は無意味にCloneNotSupportedException
を処理するコードを書かなければならなくなります。
public class DefaultClone implements Cloneable { @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
■CloneNotSupportedExceptionを実行時例外にラップする場合
CloneNotSupportedException
をtry-catchで補足してRuntimeException
(実行時例外)にラップして再スローするコード例です
実行時例外としてラップされているため、DefaultClone2#cloneを呼び出す側ではCloneNotSupportedException
に対して処理するコードを書く必要がありません。
public class DefaultClone2 implements Cloneable { @Override protected Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } }
■@SneakyThrowsを利用する場合
@SneakyThrows
を対象メソッドに付与してCloneNotSupportedException
の発生を隠蔽するコード例です
SneakyClone#cloneを呼び出す側ではCloneNotSupportedException
に対して処理するコードを書く必要がありません。
import lombok.SneakyThrows; public class SneakyClone implements Cloneable { @SneakyThrows @Override protected Object clone() { return super.clone(); } }
■実際に生成されているソースコード
@SneakyThrows
を付与したクラスから実際にどのようなソースコードが生成されるかを、delombokを使って確認してみました。
Object#clone呼び出し部分で例外のcatchが行われ、lombok.Lombok#sneakyThrow
でラップされていることが分かります
public class SneakyClone implements Cloneable { @Override protected Object clone() { try { return super.clone(); } catch (final java.lang.Throwable $ex) { throw lombok.Lombok.sneakyThrow($ex); } } }
UnsupportedEncodingExceptionを@SneakyThrowsで隠す
UnsupportedEncodingExceptionはStringのコンストラクタなどで不正なcharsetNameを指定した場合にスローされます。
ただし、取り扱い可能なcharsetNameを指定している場合でも、UnsupportedEncodingException
がチェック例外であるため何らか処理するコードを書かなければなりません。
以下に@SneakyThrows
を使わなかった場合と使った場合のコード例を記載しています。
■UnsupportedEncodingExceptionをスローする場合
UnsupportedEncodingException
を上位にスローする戦略を取った場合のコード例です
toUtf8StringメソッドがUnsupportedEncodingException
をスローするようになっていますが、内部処理のnew StringのcharsetNameに"UTF-8"を指定するので実際には発生することはありません。
それにもかかわらず、DefaultEncoding#toUtf8Stringを呼び出す側は無意味にUnsupportedEncodingException
を処理するコードを書かなければならなくなります。
import java.io.UnsupportedEncodingException; public class DefaultEncoding { public String toUtf8String(byte[] srcBytes) throws UnsupportedEncodingException { return new String(srcBytes, "UTF-8"); } }
■UnsupportedEncodingExceptionを実行時例外にラップする場合
UnsupportedEncodingException
をtry-catchで補足してRuntimeException
(実行時例外)にラップして再スローするコード例です
実行時例外としてラップされているため、DefaultEncoding2#toUtf8Stringを呼び出す側ではUnsupportedEncodingException
に対して処理するコードを書く必要がありません。
import java.io.UnsupportedEncodingException; public class DefaultEncoding2 { public String toUtf8String(byte[] srcBytes) { try { return new String(srcBytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } }
■@SneakyThrowsを利用する場合
@SneakyThrows
を対象メソッドに付与してUnsupportedEncodingException
の発生を隠蔽するコード例です
SneakyEncoding#toUtf8Stringを呼び出す側ではUnsupportedEncodingException
に対して処理するコードを書く必要がありません。
import lombok.SneakyThrows; public class SneakyEncoding { @SneakyThrows public String toUtf8String(byte[] srcBytes) { return new String(srcBytes, "UTF-8"); } }
■実際に生成されているソースコード
@SneakyThrows
を付与したクラスから実際にどのようなソースコードが生成されるかを、delombokを使って確認してみました。
new String実行部分で例外のcatchが行われ、lombok.Lombok#sneakyThrow
でラップされていることが分かります
public class SneakyEncoding { public String toUtf8String(byte[] srcBytes) { try { return new String(srcBytes, "UTF-8"); } catch (final java.lang.Throwable $ex) { throw lombok.Lombok.sneakyThrow($ex); } } }
注意点
実際に生成されているソースコードを見ると分かるのですが、実処理の中でlombok.Lombok.sneakyThrow
により例外をラップして再スローしています。
そのため、@SneakyThrows
を使う場合はアプリケーションの実行環境のクラスパスにlombok.jar
を通す必要があります