Wie kann ich eine Sammlung von Ausnahmen als Grundursache übergeben?

52

Einige Methoden myMethodrufen mehrere parallele Ausführungen auf und warten auf deren Beendigung.

Diese parallelen Ausführungen können mit Ausnahmen abgeschlossen werden. So myMethodbekommt man eine Ausnahmeliste.

Ich möchte die Ausnahmeliste als Grundursache übergeben, aber die Grundursache ist möglicherweise nur eine einzige Ausnahme. Natürlich kann ich meine eigene Ausnahme erstellen, um das zu erreichen, was ich will, aber ich möchte wissen, ob Java, Spring oder Spring Batch so etwas sofort einsatzbereit haben.

gstackoverflow
quelle
3
.NET verfügt über AggregateExceptioneine Liste mit Ausnahmen. Diese Idee sollte auch auf Java anwendbar sein.
usr
1
Siehe auch stackoverflow.com/q/8946661/821436
Monica - M. Schröder

Antworten:

49

Ich bin mir nicht sicher, ob ich es tun würde (obwohl ich Ihnen angesichts des JavaDoc nicht sagen konnte, warum ich zögere), aber es gibt eine Liste unterdrückter Ausnahmen Throwable, die Sie über hinzufügen können addSuppressed. Das JavaDoc scheint nicht zu sagen, dass dies nur für die JVM zum Ausprobieren von Ressourcen vorgesehen ist:

Hängt die angegebene Ausnahme an die Ausnahmen an, die unterdrückt wurden, um diese Ausnahme zu liefern. Diese Methode ist threadsicher und wird normalerweise (automatisch und implizit) von der Anweisung try-with-resources aufgerufen.

Das Unterdrückungsverhalten ist aktiviert, sofern es nicht über einen Konstruktor deaktiviert wird. Wenn die Unterdrückung deaktiviert ist, überprüft diese Methode nur das Argument.

Beachten Sie, dass, wenn eine Ausnahme eine andere Ausnahme verursacht, normalerweise die erste Ausnahme abgefangen wird und dann die zweite Ausnahme als Antwort ausgelöst wird. Mit anderen Worten, es besteht ein kausaler Zusammenhang zwischen den beiden Ausnahmen. Im Gegensatz dazu gibt es Situationen, in denen zwei unabhängige Ausnahmen in Geschwistercodeblöcken ausgelöst werden können, insbesondere im try-Block einer try-with-resources-Anweisung und im vom Compiler generierten finally-Block, der die Ressource schließt. In diesen Situationen kann nur eine der ausgelösten Ausnahmen weitergegeben werden. Wenn in der Anweisung try-with-resources zwei solcher Ausnahmen vorhanden sind, wird die vom try-Block stammende Ausnahme weitergegeben und die Ausnahme vom finally-Block zur Liste der Ausnahmen hinzugefügt, die durch die Ausnahme vom try-Block unterdrückt werden. In Ausnahmefällen wird der Stapel abgewickelt,

Eine Ausnahme hat möglicherweise Ausnahmen unterdrückt und wird gleichzeitig durch eine andere Ausnahme verursacht. Ob eine Ausnahme eine Ursache hat oder nicht, ist zum Zeitpunkt ihrer Erstellung semantisch bekannt, im Gegensatz dazu, ob eine Ausnahme andere Ausnahmen unterdrückt oder nicht, die normalerweise erst bestimmt werden, nachdem eine Ausnahme ausgelöst wurde.

Beachten Sie, dass vom Programmierer geschriebener Code diese Methode auch in Situationen aufrufen kann, in denen es mehrere Geschwisterausnahmen gibt und nur eine weitergegeben werden kann.

Beachten Sie den letzten Absatz, der Ihrem Fall zu entsprechen scheint.

TJ Crowder
quelle
[...] ob eine Ausnahme andere Ausnahmen unterdrückt oder nicht, [...] wird normalerweise erst bestimmt, nachdem eine Ausnahme ausgelöst wurde. Ich kann mir vorstellen, dass dies nicht der Fall sein wird, wenn mehrere unterdrückte Ausnahmen aus parallelen Läufen gesammelt werden.
GOTO 0
24

Ausnahmen und ihre Ursachen sind immer nur eine 1: 1-Sache: Sie können eine Ausnahme auslösen und jede Ausnahme kann nur eine Ursache haben (die wiederum eine Ursache haben kann ...).

Dies kann als Konstruktionsfehler angesehen werden, insbesondere wenn Sie das von Ihnen beschriebene Multithread-Verhalten berücksichtigen.

Dies ist einer der Gründe, warum Java 7 addSuppressedzu throwable hinzugefügt wurde , wodurch grundsätzlich eine beliebige Anzahl von Ausnahmen an eine einzelne andere angehängt werden kann (die andere Hauptmotivation war das Ausprobieren von Ressourcen, die eine Möglichkeit benötigten, Ausnahmen im finally-Block zu behandeln, ohne sie stillschweigend fallen zu lassen Sie).

Wenn Sie also eine Ausnahme haben, die dazu führt, dass Ihr Prozess fehlschlägt, fügen Sie diese als Ursache für Ihre übergeordnete Ausnahme hinzu. Wenn Sie eine weitere haben, fügen Sie diese mit der ursprünglichen Ausnahme hinzu addSuppressed. Die Idee ist, dass diese erste Ausnahme die anderen "unterdrückte", die Mitglied der "echten Ausnahmekette" wurden.

Beispielcode:

Exception exception = null;
for (Foobar foobar : foobars) {
  try {
    foobar.frobnicate();
  } catch (Exception ex) {
    if (exception == null) {
      exception = ex;
    } else {
      exception.addSuppressed(ex);
    }
  }
}
if (exception != null) {
  throw new SomethingWentWrongException(exception);
}
Joachim Sauer
quelle
4
Ich würde es nicht ganz so machen, wie Sie es vorschlagen, es sei denn, eine der zugrunde liegenden Ausnahmen kann wirklich als die "Hauptausnahme" herausgegriffen werden. Wenn Sie nur willkürlich eine der Ausnahmen als Hauptausnahme und die anderen als unterdrückt auswählen, laden Sie einen Anrufer ein, die unterdrückten Ausnahmen zu ignorieren und nur die Hauptausnahme zu melden - selbst wenn die "Hauptausnahme" eine TypoInUserInputException und eine von ist Die unterdrückten sind eine DatabaseCorruptedException.
Ilmari Karonen
1
… Stattdessen würde ich alle zugrunde liegenden Ausnahmen als von der SomethingWentWrongException unterdrückt markieren und dieser Ausnahme eine Meldung geben, die eindeutig angibt, dass eine oder mehrere unterdrückte Ausnahmen folgen sollten , z. B. " X von Y Aufgaben fehlgeschlagen", siehe Liste der Fehler unten ".
Ilmari Karonen