RSS-Feed anzeigen

nillehammer

Checked Exceptions weiter werfen ohne Wrappen oder throws-Klausel

Bewerten
Oft ruft man Code auf, der checked Exceptions schmeißen kann. In einigen Situationen kann in so einem Fall das Programm nicht weiterlaufen und soll beendet werden. Ein gängiges Verfahren dafür ist, die checked Exception in eine RuntimeException (oder Unterklasse wie IllegalStateException) zu wrappen und dann weiter zu werfen. Damit erspart man sich die throws-Klausel in der Methodensignatur. Beispiel:
Java Code:
  1. public Stream<String> linesFromPath(Path path) {
  2.   try {
  3.     return Files.lines(path);
  4.   } catch (IOException e) {
  5.    throw new IllegalStateException(e);
  6.   }
  7. }
Soweit nichts Weltbewegendes. An dem Verfahren sind zwei Sachen unschön. 1. Erzeugung eines im Grunde unnötigen Objekts, der IllegalStateException. 2. Die wahre Fehlerursache muss über getCause() aus der IllegalStateException herausgefuddelt werden.

Wenn man sich etwas mit dem Thema checked/unchecked Throwables beschäftigt und damit, wie Java damit umgeht, so stellt man fest, dass der Unterschied checked/unchecked etwas ist, das der Compiler prüft. Zur Laufzeit existiert dieser Unterschied nicht mehr. Es stellt sich die Frage, ob man eine checked Exception an der Compilerprüfung vorbei schmuggeln kann. Mit dem sonst viel gescholtenen Type Erasure bei Generics lässt sich das tatsächlich bewerkstelligen:
Java Code:
  1. public Stream<String> linesFromPath(Path path) {
  2.   try {
  3.     return Files.lines(path);
  4.   } catch (IOException e) {
  5.    throwSneaky(e);
  6.   }
  7.   // Compiler ausgetrickst
  8.   return null;
  9. }
  10.  
  11. @SuppressWarnings("unchecked")
  12. static <E extends Exception> void throwSneaky(Exception e) throws E {
  13.   throw (E) e;
  14. }
Auf diese Weise kann die checked Exception direkt weiter geworfen werden, ohne die Notwendigkeit, sie zu wrappen oder eine throws-Klausel zu definieren. Unschön ist allerdings, dass durch das Austricksen des Compilers im catch-Block kein throw mehr steht. Man muss also ein return Statement einbauen, das nie erreicht wird.

Diese Idee stammt nicht von mir. Ich bin auf stackoverflow darüber gestolpert. Ich wollte sie hier nur mal vor- und zur Diskussion stellen.
Stichworte: checked exception
Kategorien
Kategorielos

Kommentare

  1. Avatar von Clayn
    Wäre es eventuell eine Idee/Möglichkeit aus dem
    Java Code:
    1. @SuppressWarnings("unchecked")
    2. static <E extends Exception> void throwSneaky(Exception e) throws E {
    3.   throw (E) e;
    4. }

    ein
    Java Code:
    1. @SuppressWarnings("unchecked")
    2. static <E extends Exception> Object throwSneaky(Exception e) throws E {
    3.   throw (E) e;
    4. }

    zu machen und es dann mit
    Java Code:
    1. public Stream<String> linesFromPath(Path path) {
    2.   try {
    3.     return Files.lines(path);
    4.   } catch (IOException e) {
    5.    return throwSneaky(e);
    6.   }
    7.   // Compiler ausgetrickst
    8. }

    das ganze etwas "normaler" zu machen? Konnte es jetzt in Eclipse testen (und da auch nicht so das ich irgendwas festsetzen wöllte) und dort wurde angemekreidet, dass
    Java Code:
    1. throwSneaky(e)
    eine Exeptions deklaration oder try/catch braucht.
    War in dem Fall 1.7
  2. Avatar von nillehammer
    Zitat Zitat von Clayn
    Wäre es eventuell eine Idee/Möglichkeit aus dem ...
    Die Idee ist sehr gut. Nur nicht ganz perfekt ausprogrammiert. Ich habe Deine Idee weiterentwickelt zu:
    Java Code:
    1. @SuppressWarnings("unchecked")
    2. static <E extends Exception, T> T throwSneaky(Exception e) throws E {
    3.   throw (E) e;
    4. }
    Hinzu gekommen ist also der generische Parameter T. Damit kann man die Methode so aufrufen, wie von Dir angedacht. Und es funtioniert.
    Java Code:
    1. static Stream<String> loadLines() {
    2.   try {
    3.     return Files.lines(Paths.get("nonexistent"));
    4.   } catch (IOException e) {
    5.     return throwSneaky(e);
    6.   }
    7. }

    Edit:
    das ganze etwas "normaler" zu machen? Konnte es jetzt in Eclipse testen (und da auch nicht so das ich irgendwas festsetzen wöllte) und dort wurde angemekreidet, dass
    eine Exeptions deklaration oder try/catch braucht. [...] War in dem Fall 1.7
    Der Artikel zu dem Thema ist schon recht alt. Ich glaube also nicht, dass es an der Java-Version liegt. Getestet habe ich tatsächlich mit dem Eclipse- und dem JDK-Complier Java 8. Beide fressen das klaglos.
    Aktualisiert: 28.10.2015 um 18:00 von nillehammer
  3. Avatar von Crian
    Ich finde es spannend, dass es diese Möglichkeit gibt, werde aber wohl beim Einhüllen in (ggf. eigene) unchecked Exceptions bleiben. Meist kann man dort auch noch weitere Informationen hinzufügen, die in der Original-Ausnahme nicht auftauchen, wie Dateinamen, Zeilennummern oder ähnliches.
Kommentar schreiben Kommentar schreiben

Trackbacks