Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Java not support retrieval of exceptions from try/catch lost when an exception is thrown from finally?

In Java 7, the feature was added to (via getSuppressed()) get exceptions thrown from the implicit finally block of a try-with-resources statement.

There still doesn't seem to be a way (that I know of) to do the opposite - when there is an explicit finally block and that throws an exception, masking the exceptions thrown and pending from the try/catch.

Why does Java not provide functionality to get these buried/lost exceptions through a mechanism similar to getSuppressed()?

It would seem that the implementation of this functionality would be similar to that used in getSuppressed() or chained exceptions, and the provided benefit would be very useful, yet it continues to be left out of each release.

What would be the danger of making these masked exceptions available to programmers through a method call similar to getSuppressed()?

(Apologies in advance if this functionality already exists and I'm just clueless.)

like image 884
Mer Avatar asked Jun 12 '14 09:06

Mer


2 Answers

The suppression thing isn't limited to try-with-resources, and you can use it for similar situations yourself. E.g., it is provided for other situations.

try-with-resources puts the logic for closing the resources behind the scenes, so you don't have direct access in your own code to dealing with any exceptions that occur during the process. So they added the "suppression" thing so they could use it in that behind-the-scenes code.

But cleverly, they didn't only make it something that could be used there. You can use it yourself, via Throwable#addSuppressed.

You can see this in the pseudo-code example given in JLS §14.20.3.1; here's a real code version of it:

{
    SomeResource someResource = null;
    Throwable primaryException = null;

    try {
        someResource = /*...get the resource...*/;
        /*...do something...*/
    }
    catch (Throwable t) {
        primaryException = t;
        throw t;
    }
    finally {
        if (someResource != null) {
            if (primaryException != null) {
                // Dealing with a primary exception, close the resource
                // and suppress any exception resulting
                try {
                    someResource.close();
                }
                catch (Throwable suppressed) {
                    primaryException.addSuppressed(suppressed);
                }
            }
            else {
                // Not dealing with a primary exception, close the
                // resource without suppressing any resulting exception
                someResource.close();
            }
        }
    }
}
like image 82
T.J. Crowder Avatar answered Oct 21 '22 10:10

T.J. Crowder


Note the different behaviour (Using exception A for the exception in try, exception B in finally):

In a try-with-resources exception A suppresses exception B.
In a normal try exception B masks exception A.

If you want backwards compatibility (And you ALWAYS want it), you need to make B suppress A. But that is the complete opposite of what try-with-resources does (and actually, the opposite of what most developers want).

As a workaround, you can just use the (slightly modified) code that is in the Oracle blog on how try-with-resources works:

Exception ex;
try {
    doWork();
} catch (Exception e) {
    ex = e;
    throw(e);
} finally {
    try {
        doFinally();
    } catch (Exception e) {
        if (ex != null) {
            ex.addSuppressed(e);
        } else {
            throw(e);
        }
    }
}

Obviously move the throw out of the initial catch if you want the finally exception to suppress the initial one.

like image 38
Ordous Avatar answered Oct 21 '22 11:10

Ordous