0

Recently, I met some exception problem in java, which reminded me of the typical idiom recommended by Bruce Eckel:

Converting checked to unchecked exceptions

The real problem is when you are writing an ordinary method body, and you call another method and realize, "I have no idea what to do with this exception here, but I don’t want to swallow it or print some banal message." With chained exceptions, a new and simple solution prevents itself. You simply "wrap" a checked exception inside a RuntimeException by passing it to the RuntimeException constructor, like this:

try { // ... to do something useful } catch (IDontKnowWhatToDoWithThisCheckedException e) { throw new RuntimeException(e); } 

This seems to be an ideal solution if you want to "turn off the checked exception—you don’t swallow it, and you don’t have to put it in your method’s exception specification, but because of exception chaining you don’t lose any information from the original exception.

This technique provides the option to ignore the exception and let it bubble up the call stack without being required to write try-catch clauses and/or exception specifications.

However, I found it didn't work in some cases. as seen here:

package exceptions; // How an exception can be lost class VeryImportantException extends Exception { @Override public String toString() { return "A very important exception"; } } class HoHumException extends Exception { @Override public String toString() { return "A trivial exception"; } } public class LostMessage { void f() throws VeryImportantException { throw new VeryImportantException(); } void dispose() throws HoHumException { throw new HoHumException(); } public static void main(String[] args) { try { LostMessage lm = new LostMessage(); try { lm.f(); } catch (VeryImportantException e) { throw new RuntimeException(e); } finally { lm.dispose(); } } catch (Exception e) { throw new RuntimeException(e); } } }/* Output: Exception in thread "main" java.lang.RuntimeException: A trivial exception at exceptions.LostMessage.main(LostMessage.java:36) Caused by: A trivial exception at exceptions.LostMessage.dispose(LostMessage.java:23) at exceptions.LostMessage.main(LostMessage.java:33) *///:~ 

As the output demonstrated, the second exception completely obliterates the first one. There is no record of the first exception in the exception stack trace, which can greatly complicate debugging in real systems. usually, it’s the first exception that you want to see in order to diagnose the problem.

Joshua Bloch recommends the try-with-resource way that a resource must implement the AutoCloseable interface, which process is somewhat complex.

So, my question is this: is there any way I can use to make sure that exception will not lose its stack trace information by Bruce Eckel's approach?

5
  • 1
    You could try not wrapping it twice, for a start, but you haven't provided any proof that the stack trace is lot. Commented Sep 19, 2019 at 3:25
  • 1
    Perhaps you can build a try/catch around the main Method and in the catch you perform the traversing of the cause-list on your own and write the stac trace for all of them? Commented Sep 19, 2019 at 3:31
  • @user207421 I tried all the ways, but none of them worked. Commented Sep 19, 2019 at 3:32
  • @user207421 This is a minimal example. It is quite realistic to have nested try/catch in real code. Commented Sep 19, 2019 at 3:32
  • But it isn't 'quite realistic' to wrap an exception twice, or to throw yourself an exception within the same method, and you still haven't provided any evidence. Commented Sep 19, 2019 at 7:15

2 Answers 2

1

You might want to consider using try-with-resource instead of a finally block. It tends to handle this situation more like it sounds you would want the situation handled. See this article for more details.

Alternatively, you could simply eat the exception (as Andy Thomas's answer shows), or (if you want to know about both exceptions that were thrown) you could combine the exceptions into a single kind of Aggregate Exception.

Sign up to request clarification or add additional context in comments.

Comments

1

The problem isn't that you're wrapping the exception. The problem is that you're replacing it with a subsequent, unrelated exception thrown from the finally block.

One easy way to avoid this is to not throw an exception from the finally block.

For example:

try { LostMessage lm = new LostMessage(); try { lm.f(); } catch (VeryImportantException e) { throw new RuntimeException(e); } finally { try { lm.dispose(); } catch ( HoHumException e ) { // No-op or logging // // If we're exiting this try-finally because an exception // was thrown, then don't allow this new exception to replace it. } } } catch (Exception e) { throw new RuntimeException(e); } 

2 Comments

In your approach, the HohumException information is missing or in the log file, which is not what I want.
Another option is to store the original exception in the subsequently thrown exception, via Throwable.addSuppressed(Throwable). You can add a local variable declaration outside the try to store the first exception, if any, and set it in the first catch block, then addSuppressed() in the finally catch. But frequently it's sufficient just to communicate that the method could not do what it was asked to do, by throwing the original exception.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.