mike-neckのブログ

Java or Groovy or Swift or Golang

検査例外を検査無しで投げる邪悪な方法

netty のコードで結構前に見かけて覚えていたけど、完全に失念してて悔しかったので、メモ

呼び出し側にとってはこういうコード書かれたらかなり邪悪だと思う。

github.com

こんな感じのコード

import java.io.IOException;
import java.util.stream.Stream;

class Rethrow {

  interface IOOp {
    void run() throws IOException;
  }

  static void io(IOOp op) {
    try {
      op.run();
    } catch (IOException e) {
      rethrow(e);
    }
  }

  static void rethrow(Throwable throwable) {
    rethrow0(throwable);
  }

  @SuppressWarnings("unchecked")
  static <T extends Throwable> void rethrow0(Throwable throwable) throws T {
    throw (T)throwable;
  }

  public static void main(String... args) {
    Stream.of("catch me if you can", "(☝՞ਊ ՞)☝ウェーイwwwみてるー?")
      .map(IOException::new)
      .forEach(e -> io(() -> { throw e; }));
  }
}

rethrowrethrow0 の組み合わせがポイントになる…なお、言語仕様を詳しく調べてないので、コンパイルエラーにならない理由をうまく説明できない

実行結果はこちら

f:id:mike_neck:20200531133113p:plain

IOException により、 main が終了していることがわかる

なお、 Stream の部分を try/catch(IOException e) で囲むと、コンパイルエラーになる

f:id:mike_neck:20200531133309p:plain

したがって、このコードは検査例外を非検査で投げるが Exception/Throwable 以外の非検査例外では catch できなくなるので注意が必要