チェックされた例外で失敗する可能性のあるメソッドがあるとします(Sunの推奨に従って、回復可能であるためチェックされています)。この方法は失敗し、回復戦略をトリガーします。しかし、最初の方法と回復戦略の両方が失敗しました。
場合によっては、最後の戦略だけでなく、初期戦略と回復戦略の両方が失敗した理由を知るために、両方のスタックトレースが必要になることがあります。
私に何ができる?CompositeExceptionタイプなどを作成する必要がありますか?それは良い習慣ですか?
チェックされた例外で失敗する可能性のあるメソッドがあるとします(Sunの推奨に従って、回復可能であるためチェックされています)。この方法は失敗し、回復戦略をトリガーします。しかし、最初の方法と回復戦略の両方が失敗しました。
場合によっては、最後の戦略だけでなく、初期戦略と回復戦略の両方が失敗した理由を知るために、両方のスタックトレースが必要になることがあります。
私に何ができる?CompositeExceptionタイプなどを作成する必要がありますか?それは良い習慣ですか?
Java 7では、抑制された例外の概念が導入されました。たとえば、try-with-resourcesステートメントは次のように指定されます。
リソースは、初期化された順序とは逆の順序で閉じられます。リソースは、null以外の値に初期化された場合にのみ閉じられます。1つのリソースを閉じることによる例外は、他のリソースの閉じることを妨げるものではありません。このような例外は、初期化子、tryブロック、またはリソースのクローズによって以前に例外がスローされた場合に抑制されます。
と
リソースの初期化が正常に完了し、値Vがスローされたために、tryブロックが突然完了した場合、次のようになります。
- 値V2がスローされたためにリソースの自動クローズが突然完了した場合、V2がVの抑制された例外リストに追加された値Vがスローされたため、try-with-resourcesステートメントが突然完了します。
これはjava.lang.Throwable.addSuppressedExceptionを使用し、そのjavadocは次のようになります。
この例外を配信するために抑制された例外に、指定された例外を追加します。このメソッドはスレッドセーフであり、通常、try-with-resourcesステートメントによって(自動的および暗黙的に)呼び出されます。
コンストラクターで無効にしない限り、抑制動作は有効になります。抑制が無効になっている場合、このメソッドは引数を検証する以外に何もしません。
ある例外が別の例外を引き起こす場合、通常、最初の例外がキャッチされ、それに応じて2番目の例外がスローされることに注意してください。言い換えれば、2つの例外の間には因果関係があります。対照的に、兄弟コードブロック、特にtry-with-resourcesステートメントのtryブロックと、リソースを閉じるコンパイラー生成のfinallyブロックでは、2つの独立した例外がスローされる場合があります。このような状況では、スローされた例外の1つだけを伝播できます。try-with-resourcesステートメントで、このような例外が2つある場合、tryブロックから発生した例外が伝播され、finallyブロックからの例外が、tryブロックからの例外によって抑制された例外のリストに追加されます。例外として、スタックを巻き戻します。
例外によって例外が抑制された可能性がありますが、別の例外が原因である可能性もあります。例外に原因があるかどうかは、例外がスローされた後にのみ通常決定される他の例外を抑制するかどうかとは異なり、その作成時に意味的にわかります。
プログラマーが作成したコードは、複数の兄弟例外があり、1つしか伝播できない状況で、このメソッドを呼び出すことも利用できることに注意してください。
その最後の段落はあなたの状況に当てはまるようです。だからあなたはすることができます:
public static void main(String[] args) throws Exception {
try {
throw new RuntimeException("Not now!");
} catch (Exception e) {
try {
tryAgain();
} catch (Exception e2) {
e.addSuppressed(e2);
throw e;
}
}
}
次に、スタックトレースには両方の例外が含まれます。
Exception in thread "main" java.lang.RuntimeException: Not now!
at tools.Test.main(Test.java:12)
Suppressed: java.lang.RuntimeException: I'm on holiday.
at tools.Test.tryAgain(Test.java:7)
at tools.Test.main(Test.java:15)
ただし、呼び出し元がキャッチできるのは主要な例外のみです。
「両方のスタックトレースが必要な場合がある」とは、正確にはどういう意味ですか?私が行っているのは、両方をログに記録することです。これらは「発生してはならないこと」だからです。ログを確認し、何が問題なのかを追跡できます。「CompositeException」を実行すると、すぐに組み合わせの問題が発生します。「CompositeExceptionHandler」も失敗した場合はどうなりますか?私は...するだろう...
そうすれば、カップリングを改善できます。両方のレベルを個別に処理します
最初の方法が失敗した場合と、回復戦略が失敗した場合の例外を検討します。
リカバリが失敗したという例外をスローする前に、リカバリ戦略を複数回試すようなことをしたい場合があります(たとえば、タイムアウトが発生したときに再試行して失敗します)。
最初のメソッドとリカバリ戦略の例外を分離しておくことは、2つの異なるシナリオを表すため、理にかなっているようです。
残念ながら、JavaException
の原因は1つしかないため、aCompositeException
が最善の選択肢かもしれません。ただし、メソッドをオーバーライドして、複合例外のスタックトレースを出力できます。printStackTrace
public class CompositeException extends Exception {
private final List<Throwable> causes = new ArrayList<Throwable>();
public CompositeException(Throwable... causes) {
this.causes.addAll(Arrays.asList(causes));
}
// Other constructors you want
@Override
public void printStackTrace() {
if (causes.isEmpty()) {
super.printStackTrace();
return;
}
for (Throwable cause : causes) {
cause.printStackTrace();
}
}
@Override
public void printStackTrace(PrintStream s) {
if (causes.isEmpty()) {
super.printStackTrace(s);
return;
}
for (Throwable cause : causes) {
cause.printStackTrace(s);
}
}
@Override
public void printStackTrace(PrintWriter s) {
if (causes.isEmpty()) {
super.printStackTrace(s);
return;
}
for (Throwable cause : causes) {
cause.printStackTrace(s);
}
}
}
これは、1つの例外で複数の原因の例外を発生させるためにできることのほぼすべてです。それを使用するには:
CompositeException ce = new CompositeException(ex1, ex2, ex3);
次にce.printStackTrace()
、3つの例外すべてのスタックトレースを出力します。