Exception Handling & Automatic Resource Management

Java 7: Project Coin II

Angelika Langer und Klaus Kreft

Die Version 7 von Java bringt eine Reihe von kleineren Sprachergänzungen mit sich, die unter dem Namen Project Coin entwickelt wurden. Im diesem Artikel stellen wir den zweiten Teil von Project Coin vor: Verbesserungen beim Exception Handling und die automatische Ressourcen-Verwaltung.

Project Coin ist eine Sammlung kleinerer Spracherweiterungen. Die Idee dabei war es, die Sprache um Mittel zu erweitern, die nützlich und „klein“ sind. Folgende neue Sprachmittel wurden mit Java 7 freigegeben:

  • Verbesserte Schreibweisen für numerische Literale
  • Strings in switch-Anweisungen
  • Verbessertes Exception Handling
  • Automatic Resource Management
  • Vereinfachungen beim Aufruf von Methoden mit variabler Argumentenliste
  • Verbesserte Type Inference für Konstruktoren generischer Typen (Diamond Operator)

Die ersten beiden Punkte hatten wir in unserem ersten Artikel zu Project Coin vorgestellt. Dieses Mal geht es um das verbesserte Exception Handling und das Automatic Resource Management.

Verbessertes Exception Handling

Die Verbesserungen beim Exception Handling bestehen in einer Flexibilisierung beim Rethrow von Exceptions und einer Multi-Catch-Klausel [2].

Flexibilisierung beim Rethrow von Exceptions: Manchmal will man eine catch-Klausel schreiben, in der alle Exceptions, egal von welchem Typ, gefangen werden, damit ein Cleanup gemacht werden kann, und danach wird die ursprüngliche Exception weiter geworfen. In einer solchen catch-Klausel würde man gerne Throwable fangen, um alle Exception-Typen zu erwischen. Daraus ergibt sich beim Rethrow ein Problem: Der Compiler ist aufgrund der statischen Typinformation Throwable der Meinung, die Methode werfe nun beliebige Exceptions vom Typ Throwable, und gibt eine Fehlermeldung aus. In Listing 1 sehen Sie ein Beispiel.

Listing 1
public void someMethod throws SomeSpecificException {
        .
        try {
            doWork(file);  // might throw SomeSpecificException
        } catch (Throwable ex) {
            // some cleanup activity
            throw ex;    // error
          }
       .
    }
___________________________________________________
unreported exception Throwable; must be caught or 
                                 declared to be thrown
            throw ex;

In Java 7 werden die Compilerprüfungen dahingehend geändert, dass der Compiler beim Rethrow nicht mehr die statische Typinformation der gefangenen Exception zugrunde legt, sondern davon ausgeht, dass lediglich die Exception-Typen beim Rethrow vorkommen können, die in dem try-Block geworfen werden können. Das sieht dann folgendermaßen aus:

    public void someMethod throws SomeSpecificException {
        .
        try {
            doWork(file);  // might throw SomeSpecificException
        } catch (Throwable ex) {
            // some cleanup activity
            throw ex;     // fine
        }
        .
    }

Nun muss man die throws-Klausel der Methode nicht mehr ändern und throws Throwable deklararieren, sondern die throws-Klausel der Methode kann wie gewünscht die spezifischen, tatsächlich zu erwartenden Exception-Typen deklarieren.

Allerdings darf man an die Referenzvariable ex im catch-Block nichts zuweisen, wenn man einen Rethrow machen will. Sobald im catch-Block an die Referenzvariable ex zugewiesen wird, weigert sich der Compiler, einen Rethrow zu akzeptieren. Der Compiler erkennt nämlich daran, dass der Referenzvariablen im catch-Block nichts Neues zugewiesen wurde, dass es sich um einen Rethrow der Original-Exception handelt und dass nicht etwa eine andere Exception geworfen wird. Wenn man eine Zuweisung Referenzvariable im catch-Block macht, dann ist es eben kein Rethrow mehr.

In diesem Fall behandelt der Compiler die Exception dann so, wie früher in Java 6 schon: er beschwert sich darüber, dass die Methode eine Exception vom Typ Throwable wirft, diesen Exception-Typ aber nicht in ihrer throws-Klausel deklariert hat und dann muss man die throws-Klausel der Methode ändern und throws Throwable deklararieren.

Man sich sollte deshalb merken, dass die Referenzvariable ex im catch-Block „effectively final“ sein muss, wenn man einen Rethrow machen will. Das heißt, man muss die Variable zwar nicht ausdrücklich als final deklarieren, aber man sollte sie dennoch wie eine final-Variable behandeln.

Multi-Catch: Mit dem Multi-Catch soll redundanter Code vermieden werden. Anstelle von:

    public void someMethod throws IOException,InterruptedException {
        .
        try {
            doWork(file);
        } catch (IOException ex) {
            logger.log(ex);
            throw ex;
        }catch (InterruptedException ex) {
            logger.log(ex);
            throw ex;
        }
        .
    }

darf man nun catch-Klauseln mit identischem Code zu einer einzigen Klausel zusammenfassen:

    public void someMethod throws IOException,InterruptedException {
        .
        try {
            doWork(file);
        } catch (IOException | InterruptedException ex) {
            logger.log(ex);
            throw ex;
        }
        .
    }

Eine weitere Alternative für die Implementierung, die bereits heute genutzt werden kann, sieht folgendermaßen aus:

    public void someMethod throws Exception {
        .
        try {
            doWork(file);
        } catch (Exception ex) {
            logger.log(ex);
            throw ex;
        }
        .
    }

Der Nachteil ist, dass mehr Exception-Typen gefangen werden, als man eigentlich will. Zusätzlich wird die Deklaration der throws-Klausel der Methode sehr unspezifisch, da der Exception-Supertyps verwendet werden muss. Mit dem Multi-Catch entfällt sowohl die Redundanz als auch die unspezifische throws-Klausel.

Es gibt eine Randbedingung bei der Verwendung des neuen Multicatch-Features: die Referenzvariable ex im catch-Block ist „effectively final“. Das heißt, man muss die Variable nicht ausdrücklich als „final IOException | InterruptedException ex“ deklarieren, aber sie wird dennoch wie eine final-Variable behandelt und man darf an diese Variable nichts zuweisen. Wenn man es trotzdem tut, bekommt man eine Fehlermeldung vom Compiler, die besagt, man dürfe an den Multicatch-Parameter nichts zuweisen.

Geschrieben von
Angelika Langer und Klaus Kreft
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.