web-dev-qa-db-ja.com

チェックされた例外をラップするが、元のランタイム例外をJavaに保持する方法

チェック例外と実行時例外の両方をスローする可能性のあるコードがいくつかあります。

チェックされた例外をキャッチし、ランタイム例外でラップしたいのですが。しかし、RuntimeExceptionがスローされた場合、それはすでにランタイム例外であるため、ラップする必要はありません。

私が持っているソリューションは少しオーバーヘッドがあり、「きれい」ではありません:

try {
  // some code that can throw both checked and runtime exception
} catch (RuntimeException e) {
  throw e;
} catch (Exception e) {
  throw new RuntimeException(e);
}

よりエレガントな方法のアイデアはありますか?

27

「ブラインド」再スローを使用して、チェックされた例外を渡します。チェック済みの例外をスローするラムダを使用できないStreams APIを通過するためにこれを使用しました。たとえば、ThrowingXxxxx機能インターフェースがあるため、チェックされた例外を通過させることができます。

これにより、呼び出し先でチェックされた例外を、呼び出し先がチェックされた例外を許可しないインターフェイスを介して渡す必要があることを知らなくても、自然にキャッチできます。

try {
  // some code that can throw both checked and runtime exception

} catch (Exception e) {
  throw rethrow(e);
}

呼び出しメソッドで、チェックされた例外を再度宣言できます。

public void loadFile(String file) throws IOException {
   // call method with rethrow
}

/**
 * Cast a CheckedException as an unchecked one.
 *
 * @param throwable to cast
 * @param <T>       the type of the Throwable
 * @return this method will never return a Throwable instance, it will just throw it.
 * @throws T the throwable as an unchecked throwable
 */
@SuppressWarnings("unchecked")
public static <T extends Throwable> RuntimeException rethrow(Throwable throwable) throws T {
    throw (T) throwable; // rely on vacuous cast
}

例外を処理するためのさまざまなオプションがたくさんあります。それらのいくつかを使用します。

https://Vanilla-Java.github.io/2016/06/21/Reviewing-Exception-Handling.html

28
Peter Lawrey

Guavaの Throwables.propagate() はまさにこれを行います:

try {
    // some code that can throw both checked and runtime exception
} catch (Exception e) {
    throw Throwables.propagate(e);
}

更新:このメソッドは廃止されました。詳細な説明については このページ を参照してください。

18
shmosel

あんまり。

これを頻繁に行うと、ヘルパーメソッドに組み込むことができます。

static RuntimeException unchecked(Throwable t){
    if (t instanceof RuntimeException){
      return (RuntimeException) t;
    } else if (t instanceof Error) { // if you don't want to wrap those
      throw (Error) t;
    } else {
      return new RuntimeException(t);
    }
}

try{
 // ..
}
catch (Exception e){
   throw unchecked(e);
}
5
Thilo

以下を含む特別にコンパイルされた.classファイルがあります。

public class Thrower {
    public static void Throw(Java.lang.Throwable t) {
        throw t;
    }
}

うまくいきます。 Javaコンパイラは通常、これをコンパイルすることを拒否しますが、バイトコードベリファイアはまったく気にしません。

このクラスは、Peter Lawreyの回答と同様に使用されます。

try {
  // some code that can throw both checked and runtime exception

} catch (Exception e) {
    Thrower.Throw(e);
}
2
Joshua

私は一般的に同じタイプのコード構造を使用しますが、3項演算子が実際にコードを改善する数回のうちの1つでそれを1行に圧縮します。

try {
  // code that can throw
}
catch (Exception e) {
  throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);
}

これは追加のメソッドやcatchブロックを必要としないので、私はそれが好きです。

1
user439793

Instanceof演算子を使用して同じように書き換えることができます

try {
    // some code that can throw both checked and runtime exception
} catch (Exception e) {
    if (e instanceof RuntimeException) {
        throw e;
    } else {
        throw new RuntimeException(e);
    }
}

ただし、ソリューションはより良く見えます。

1
Tionio

問題は、Exceptionが広すぎることです。チェックされる可能性のある例外を正確に把握する必要があります。

try {
    // code that throws checked and unchecked exceptions
} catch (IOException | SomeOtherException ex) {
    throw new RuntimeException(ex);
}

これが機能しない理由は、代わりに対処する必要があるより深い問題を明らかにします。

メソッドがそれを宣言している場合throws Exceptionそれでは広すぎます。これ以上の情報がない状態で「何か問題が発生する可能性がある」ことを知っていても、発信者には役に立ちません。メソッドは、意味のある階層で特定の例外クラスを使用するか、必要に応じてチェックされていない例外を使用する必要があります。

メソッドがチェックする例外の種類が多すぎる場合は、複雑すぎます。状況に応じて、複数の単純なメソッドにリファクタリングするか、例外を適切な継承階層に配置する必要があります。

もちろん、ルールには例外があります。メソッドの宣言throws Exceptionは、他の人が使用するためのAPIを構成するのではなく、ある種の分野横断的なフレームワーク(JUnit、AspectJ、Springなど)によって使用される場合、完全に合理的です。

1
OrangeDog