Will
Will

Reputation: 5537

Idiomatic Java "finally" block that only runs on exception

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;
}

Upvotes: 4

Views: 168

Answers (3)

luis.espinal
luis.espinal

Reputation: 10529

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

Languages have idiomatic constructs unique to the syntax to stuff. In Java, the first option you showed:

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

it is the idiomatic way of doing it in Java that feels natural to the (no pun intended) natural syntax of said language.

You are looking for a natural on-error only mechanism, and Java does not have one. You are trying create a clean-up mechanism without involving compiler checks on checked throwables. One would think to use generics, but generics have restrictions (namely, one cannot parameterize a subclas of throwable.)

However, IIRC, there are ways to circumvent compiler checking by parameterizing the enclosing method with a Class, then manufacture the instance via reflection and throwing it. But, in the general case, now we are getting into the realm of the hairy for no apparent and useful reason.

I would stick to the example you showed. It is clear what it does, it is clean and there are no asinine and unexpected gotchas in it.

.ps.

For the love of God, I hope you are not using C++ catch-all idiom (at least not without preceding it with with a battery of type-specific catch blocks, including std::exception).

There is nothing worse than having to deal with code that reaches such a catch-all block. There is no freaking information whatsoever, near impossible to trace what threw it.

Been there and don't that for a living. The stuff of nightmares.

Upvotes: 0

Marko Topolnik
Marko Topolnik

Reputation: 200168

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 {}

Upvotes: 6

rgettman
rgettman

Reputation: 178263

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;
}

Upvotes: 4

Related Questions