Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic Java "finally" block that only runs on exception

Tags:

java

If you have some cleanup code that needs to run regardless of whether an exception was thrown, try { ... } finally { ... } is what you need. But what if you want that cleanup code to only run in the event of an exception?

One possible solution is this:

boolean ok = false;
try {
    doWork();
    ok = true;
} finally {
    if (!ok) {
        doCleanup();
    }
}

It does exactly what I want, but I wondered if there was a more natural way to express the idea.

This alternative doesn’t pass muster:

try {
    doWork();
} catch (Throwable e) {
    doCleanup();
    throw e; // BAD: Cant do this unless the enclosing function is declared to throw Throwable
}

I suppose I could catch Exception instead of Throwable, but I don't like the idea that a non-Exception throwable will bypass the cleanup code. After all, a regular finally block still runs even for non-Exception throwables.

Aside: In C++, you would simply use a catch-all block:

try {
    doWork();
} catch (...) {
    doCleanup();
    throw;
}
like image 511
Will Avatar asked Dec 19 '22 10:12

Will


2 Answers

As of Java 7 this is legal code:

public static void method() {
  try {
    // stuff which doesn't declare checked exceptions
  } catch (Throwable t) { 
    throw t; 
  }
}

The improved language rules allow this because static analysis can prove that t will not actually be any checked exception.

Likewise, you are only required to declare the minimum necessary checked exceptions:

public static void method() throws IOException {
  try {
    throwingMethod();
  } catch (Throwable t) { throw t; }
}

static void throwingMethod() throws IOException {}
like image 175
Marko Topolnik Avatar answered Dec 21 '22 23:12

Marko Topolnik


If you have a try...finally block, then one of two cases applies:

  1. You have a throws clause on the method, and the try block may throw a checked exception.

Have a catch block that catches RuntimeExceptions and any checked exceptions:

catch (RuntimeException | YourCheckedException e) {
    // Cleanup
    throw e;
}
  1. You don't have a throws clause, and the try block may only throw an unchecked exception.

Catch RuntimeException, which doesn't need to be declared in throws.

catch (RuntimeException e) {
    // Cleanup
    throw e;
}
like image 22
rgettman Avatar answered Dec 22 '22 00:12

rgettman