Java Multiple catch-Blöcke und Multi-catch
Verschiedene Ausnahmetypen in Java mit mehreren catch-Blöcken oder einem einzigen Multi-catch (Type1 | Type2 e) abfangen.
Einem einzelnen try können beliebig viele catch-Blöcke folgen. Jeder deklariert einen anderen Ausnahmetyp und wird nur ausgeführt, wenn der Wurf übereinstimmt. So kann Java auf einen Codeblock unterschiedlich reagieren, je nachdem, was schiefgelaufen ist – ein Netzwerkfehler ist kein Parse-Fehler, und Sie möchten diese möglicherweise auf völlig unterschiedliche Weise behandeln.
Mehrere sequenzielle catches
try {
String body = httpClient.get(url);
Config cfg = parser.parse(body);
apply(cfg);
} catch (IOException e) {
retryLater(url);
} catch (ParseException e) {
report("config file is malformed: " + e.getMessage());
} catch (SecurityException e) {
report("not allowed to apply config");
}Pro Wurf wird nur ein catch ausgeführt – der erste, dessen Typ eine instanceof-Übereinstimmung mit der geworfenen Ausnahme hat. Nachdem er ausgeführt wurde, springt die Kontrolle zur Anweisung nach der gesamten try-Anweisung, nicht zum nächsten catch.
Reihenfolge ist wichtig: vom Spezifischen zum Allgemeinen
Ein catch (T e) passt auf alles, was ein T oder eine Unterklasse von T ist. Wenn Sie eine Oberklasse vor ihrer Unterklasse auflisten, ist der Unterklassen-catch nicht erreichbar, und der Compiler lehnt das ab:
try { ... }
catch (Exception e) { ... } // matches everything below Exception
catch (IOException e) { ... } // ERROR: unreachableDie einzuhaltende Reihenfolge ist engster Typ oben, weitester unten:
try { ... }
catch (FileNotFoundException e) { ... } // most specific
catch (IOException e) { ... } // wider
catch (Exception e) { ... } // catch-all (used sparingly)Dies ist auch eine nützliche Designübung: Das Schreiben der catches zwingt Sie, darüber nachzudenken, welche Fehler der Block tatsächlich erzeugen kann.
Multi-catch (ein Block, mehrere Typen)
Java 7 hat die Multi-catch-Form hinzugefügt: ein Block, der mehrere nicht verwandte Ausnahmetypen behandelt, getrennt durch |:
try {
return parser.read(file);
} catch (IOException | ParseException e) {
log.warn("could not load config: " + e);
return Config.defaults();
}Verwenden Sie dies, wenn die Behandlung für mehrere verschiedene Fehler identisch ist. Es ist kürzer als zwei nahezu doppelte catch-Blöcke und macht die Beziehung „diese und diese teilen eine Antwort" auf einen Blick offensichtlich.
Zu beachtende Regeln:
- Die Typen in der Union dürfen nicht durch Vererbung verwandt sein.
IOException | FileNotFoundExceptionwird nicht kompilieren – einer ist ein Untertyp des anderen, sodass der weitere ihn bereits abdeckt. - Innerhalb des Blocks ist
eals gemeinsamer Supertyp der aufgelisteten Typen typisiert. Sie können Methoden aufrufen, die auf diesem Supertyp deklariert sind, aber keine untertyp-spezifischen. Für die meisten Anwendungen (Logging, Wrapping) sindgetMessage()undtoString()ausreichend. - Der catch-Parameter in einem Multi-catch ist implizit final – Sie können ihn nicht neu zuweisen. (Single-Typ-catches sind nur effektiv final; der Unterschied spielt in der Praxis keine Rolle.)
Wann ein try aufgeteilt werden sollte
Ein häufiger Lesbarkeits-Fehler ist es, eine ganze Methode in ein einziges riesiges try zu verpacken und dann alles abzufangen, was irgendeine Zeile werfen könnte. Die Behandlungslogik am Ende ist dann verwirrt, welche Zeile fehlgeschlagen ist.
Zwei sauberere Formen, wenn das passiert:
- Zwei separate try-Anweisungen, jede auf eine zusammengehörige Menge von Operationen beschränkt.
- Ein try innerhalb einer Methode, die kleinere Methoden aufruft, jede verantwortlich für eine Art von Fehler.
Je kleiner das try, desto einfacher lassen sich die catches nachvollziehen. „Welche Zeile könnte das werfen?" sollte immer eine kurze Antwort haben.
Ein ausgearbeitetes Beispiel
Ein kleines Programm, das drei Dinge tut – eine Zahl parsen, sie in einem Array nachschlagen, durch sie dividieren – von denen jedes auf seine eigene Weise fehlschlagen kann. Wir fangen jeden Fehler mit einem dedizierten Handler ab, damit die Meldungen spezifisch bleiben. Der letzte catch verwendet die Multi-catch-Form, um zwei Fehler zu gruppieren, die eine gemeinsame Antwort teilen.
Jede Eingabe nimmt einen anderen Weg durch die catches, aber jede Iteration gibt eine saubere Zeile aus – das Programm gibt dem Benutzer niemals auf.
Was kommt als Nächstes
try/catch behandelt den Happy Path und den Fehlerpfad. Die dritte Klausel, finally, behandelt die Dinge, die in jedem Fall passieren müssen. Fahren Sie fort mit Java finally-Block.