4

Firstly, sorry for asking a question on what must be a well-worn topic. I've come across loads of questions with answers suggesting to swallow (catch and ignore/log) potential exception from methods that close resources in finally blocks. It seems to be a generally accepted pattern. But I haven't yet seen anybody explain why. Here's just one example: Try-catch-finally and then again a try catch.

I understand that any exception thrown from a finally block will "mask" any exception thrown in the corresponding try block, but I don't see why this is a bad thing. For example:

Resource r = new Resource();
try {
    r.use();
    other();
} finally {
    r.close();
}

My current understanding is:

  1. If only close throws an exception, we definitely don't want to swallow it.
  2. If both use and close throw an exception, it's probably for the same underlying reason, and it doesn't matter which exception gets propagated up (they both would contain equally useful information?).
  3. If both other and close throw an exception, there are two unrelated problems. Arguably the one that happened first should propagate, but there isn't any reason to suggest that the first exception caused the second (is there?), so either will do.

What am I wrong about?

Community
  • 1
  • 1
AmyGamy
  • 101
  • 7

1 Answers1

3

If only close throws an exception, we definitely don't want to swallow it.

That's right!

If both use and close throw an exception, it's probably for the same underlying reason, and it doesn't matter which exception gets propagated up (they both would contain equally useful information?).

Not really. The exception in try could have been NullPointerException, OutOfMemoryError or actual I/O exception describing the nature of the problem. Even if it's an I/O exception, the error could have left the stream in inconsistent state (or even closed it prematurely). Calling close() might yield completely different error, like "stream already closed", "internal error" or whatever.

One error in the system tends to cascade dozens of others, sometimes looking very unrelated. Always look for the first exception for the root cause. The rest is just garbage. A good example is an initialization that failed and left an object in inconsistent state. Later you see NullPointerExceptions everywhere, but the real problem occurred way earlier.

If both other and close throw an exception, there are two unrelated problems. [...] there isn't any reason to suggest that the first exception caused the second (is there?), so either will do.

Actually, both exceptions are imports, but the first one is probably more relevant. Solution? Use AutoCloseable resources in Java 7 and try-with-resources idiom. If exception occurs while cleaning up, it will be attached to original exception as suppressed:

class Resource implements AutoCloseable  //...

and then:

try(Resource r = new Resource()) {
    r.use();
    other();
}

The exception will look something like this:

MyException: Exception in use()
at Resource.use(Main.java:11)
at Main.main(Main.java:16)
    Suppressed: java.lang.RuntimeException: Exception in close()
        at Main.close(Main.java:6)
        at Main.main(Main.java:17)
        }
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • The key thing for me is that you say the close might return a completely different error. However, can you provide an example where this is true? See my edit to the original question. – AmyGamy Oct 02 '12 at 22:12
  • ...Decided not to edit. I tested what happens with file access and a network disconnect and close didn't actually throw anything (perhaps unsurprisingly). – AmyGamy Oct 02 '12 at 22:35