web-dev-qa-db-ja.com

別の例外にネストされている例外をキャッチする

別の例外にネストされている例外をキャッチしたい。私は現在このようにしています:

} catch (RemoteAccessException e) {
    if (e != null && e.getCause() != null && e.getCause().getCause() != null) {
        MyException etrp = (MyException) e.getCause().getCause();
        ...
    } else {
        throw new IllegalStateException("Error at calling service 'service'");
    }
}

これをより効率的でエレガントにする方法はありますか?

26
user321068

ネストされた例外を選択的に「キャッチ」する、より洗練された方法はありません。このようなネストされた例外をたくさんキャッチすると、コードを共通のユーティリティメソッドにリファクタリングできる可能性があります。しかし、それでもエレガントでも効率的でもありません。

エレガントなソリューションは、ネストの例外をなくすことです。最初に例外をチェーンしないか、(選択的に)スタックを解除して、ネストされた例外をスタックのさらに上に再スローします。

例外は3つの理由でネストされる傾向があります。

  1. 元の例外の詳細がアプリケーションのエラー回復に役立つ可能性は低いと判断しましたが、それらを診断目的で保持したいと考えています。

  2. 特定のチェック例外を許可しないAPIメソッドを実装していますが、コード不可避がその例外をスローします。一般的な回避策は、チェックされていない例外内にチェックされた例外を「密輸」することです。

  3. メソッドシグネチャで多数のチェック済み例外が発生するのを避けるために、怠惰で、無関係な例外の多様セットを単一の例外に変換しています1

最初のケースでは、ラップされた例外を区別する必要がある場合、最初の仮定は正しくありませんでした。最善の解決策は、メソッドシグネチャを変更して、ネストを解消できるようにすることです。

2番目のケースでは、コントロールが問題のあるAPIメソッドを渡したらすぐに例外をラップ解除する必要があります。

3番目のケースでは、例外処理戦略を再考する必要があります。つまり、適切に行う2


1-実際、Java 7.にマルチ例外キャッチ構文が導入されたため、これを行う正当な理由の1つはなくなりました。

2-APIメソッドをthrows Exceptionに変更しないでください。それは事態を悪化させるだけです。メソッドを呼び出すたびに、Exceptionを「処理」または伝達する必要があります。ガンです...

26
Stephen C

ExceptionUtils#getRootCause() メソッドは、このような状況で非常に役立ちます。

19
user321068

e.getCause().getCause()が本当にMyExceptionであるかどうかを確認するチェックを追加する必要があります。そうでない場合、このコードはClassCastExceptionをスローします。私はおそらくこれを次のように書くでしょう:

} catch(RemoteAccessException e) {
    if(e.getCause() != null && e.getCause().getCause() instanceof MyException) {
        MyException ex = (MyException)e.getCause().getCause();
        // Do further useful stuff
    } else {
        throw new IllegalStateException("...");
    }
}
18
Marc

私はこのような問題を、causeed-byチェーン全体をチェックする単純なユーティリティメソッドを作成することで解決しました。

  /**
   * Recursive method to determine whether an Exception passed is, or has a cause, that is a
   * subclass or implementation of the Throwable provided.
   *
   * @param caught          The Throwable to check
   * @param isOfOrCausedBy  The Throwable Class to look for
   * @return  true if 'caught' is of type 'isOfOrCausedBy' or has a cause that this applies to.
   */
  private boolean isCausedBy(Throwable caught, Class<? extends Throwable> isOfOrCausedBy) {
    if (caught == null) return false;
    else if (isOfOrCausedBy.isAssignableFrom(caught.getClass())) return true;
    else return isCausedBy(caught.getCause(), isOfOrCausedBy);
  }

これを使用する場合、代替のelse節を使用して、最も具体的な例外から最も具体的でないものまでのifのリストを作成するだけです。

try {
  // Code to be executed
} catch (Exception e) {
  if (isCausedBy(e, MyException.class)) {
    // Handle MyException.class
  } else if (isCausedBy(e, AnotherException.class)) {
    // Handle AnotherException.class
  } else {
    throw new IllegalStateException("Error at calling service 'service'");
  }
}

例外処理を効率的でエレガントにしたい理由はないと思いますが、私は効果的に解決します。それらは理由のために例外と呼ばれています。

このコードは、メンテナンスの悪夢になります。関心のある例外をスローするようにコールスタックを再設計できませんか?それが重要である場合、メソッドシグネチャはそれを表示し、他の2つの例外にラップされて非表示にしないでください。

最初の(e!= null)は不要です。

そして、あなたは3番目をe.getCause()。getCause()MyExceptionのインスタンスに変更することができます)

2
Peter Tillemans

あなたは以下のように行うことができます:

catch (RemoteAccessException e) {
    int index = ExceptionUtils.indexOfThrowable(e, MyExcetption.class)
    if (index != -1) {
         //handleMyException
    } else {
    }
}
1
Shrikant Lohiya

疑わしいですが、例外が正しいタイプであるかどうかをinstanceofで確認できます。

編集:ネストされた例外がラップされる理由があるはずです。そのため、ネストされた例外をキャッチする目的は何であるかを自問する必要があります。

0
Petar Minchev