Javaでの「卑劣な投げ」

Javaの「スニーキースロー」

1. 概要

Javaでは、sneaky throwの概念により、メソッドシグネチャで明示的に定義しなくても、チェックされた例外をスローできます。 これにより、throws宣言を省略でき、ランタイム例外の特性を効果的に模倣できます。

この記事では、いくつかのコード例を見て、これが実際にどのように行われるかを見ていきます。

2. 卑劣なスローについて

Checked exceptions are part of Java, not the JVM.バイトコードでは、制限なしでどこからでも例外をスローできます。

Java 8は、許可されるたびにthrows TRuntimeExceptionとして推論されることを示す新しい型推論規則をもたらしました。 これにより、ヘルパーメソッドなしで巧妙なスローを実装できます。

sneaky throwsの問題は、おそらく最終的に例外をキャッチしたいということですが、Javaコンパイラでは、特定の例外タイプの例外ハンドラを使用して、こっそりとスローされたチェック済み例外をキャッチできません。

3. 卑劣なスローの実行

すでに述べたように、コンパイラとJavaランタイムは異なるものを見ることができます。

public static  void sneakyThrow(Throwable e) throws E {
    throw (E) e;
}

private static void throwsSneakyIOException() {
    sneakyThrow(new IOException("sneaky"));
}

The compiler sees the signature with the throws T inferred to a RuntimeException typeであるため、チェックされていない例外を伝播できます。 すべてのスローは同じ単純なthrow e.であるため、Javaランタイムはスローにタイプを認識しません。

この簡単なテストは、シナリオを示しています。

@Test
public void whenCallSneakyMethod_thenThrowSneakyException() {
    try {
        SneakyThrows.throwsSneakyIOException();
    } catch (Exception ex) {
        assertEquals("sneaky", ex.getMessage().toString());
    }
}

バイトコード操作またはThread.stop(Throwable)を使用してチェックされた例外をスローすることは可能ですが、面倒なのでお勧めできません。

4. ロンボクアノテーションの使用

Lombok@SneakyThrowsアノテーションを使用すると、throws宣言を使用せずにチェック済み例外をスローできます。 これは、Runnable.のような非常に制限されたインターフェイス内のメソッドから例外を発生させる必要がある場合に便利です。

Runnable内から例外をスローするとします。 Thread’sの未処理の例外ハンドラーにのみ渡されます。

このコードはExceptionインスタンスをスローするため、RuntimeException:でラップする必要はありません。

public class SneakyRunnable implements Runnable {
    @SneakyThrows(InterruptedException.class)
    public void run() {
        throw new InterruptedException();
    }
}

このコードの欠点は、宣言されていないチェック済みの例外をキャッチできないことです。 so, it will not compile

卑劣な例外をスローするための正しい形式は次のとおりです。

@SneakyThrows
public void run() {
    try {
        throw new InterruptedException();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

そして、これがこの振る舞いのテストです:

@Test
public void whenCallSneakyRunnableMethod_thenThrowException() {
    try {
        new SneakyRunnable().run();
    } catch (Exception e) {
        assertEquals(InterruptedException.class, e.getStackTrace());
    }
}

5. 結論

この記事で見たように、Javaコンパイラーをだまして、チェック済み例外を未チェックとして扱うことができます。

いつものように、コードはover on GitHubで利用できます。