Question

I just ran into a hidden gem in one of our Java libraries:

for(Widget w : widgets) {
    if(shouldDoStuff()) {
        try{
            // Do stuff to w.
        } catch(Exception e){
            throw new RuntimeException("Couldn't do stuff.");
        } finally{
            // Compiler warning: finally block does not complete normally
            continue;
        }
    }
}

I know that finally trumps everything, but I'm wondering about 2 things:

  1. What happens when the catch clause does execute? Does the exception get thrown or not? What happens first: the thrown exception or the continue statement?
  2. How can I rewrite this to get rid of the warning?

I found this very similar question but the accepted answer just states that the exception will be thrown abruptly, and I'm not sure what that means. Plus it doesn't really help me understand my first question above about the order of events that will transpire.

Was it helpful?

Solution

"finally" will be executed after your RuntimeException is thrown and before it is processed by another catch upper in the stack.

As your finally just continues, in fact it will do nothing.

The contradiction is between the throw in the catch that will end the loop and the continue.

One approach could be:

boolean exceptionOccured = false;
for(Widget w : widgets) {
    if(shouldDoStuff()) {
        try {
            // Do stuff to w.
        } catch(Exception e){
            exceptionOccured = true;  // do not throw yet.
            e.printStackTrace();
        }
    }
}
if (exceptionOccured) {
  throw new RuntimeException("Couldn't do stuff.");
}

The main concern with this approach is you don't know what went wrong.

OTHER TIPS

The exception is thrown. If ran, you would see:

  1. RuntimeException("Couldn't do stuff.")
  2. Do what's in finally.

If there was an outer try/catch that caught the RuntimeException, then the program could feasibily continue. The continue statement in the finally block is useless. The only time finally won't be called is if you call System.exit() or if the JVM crashes first.

Code of the form

try {
  // non-control-flow statements A
} finally {
  // non-control-flow statements B
}

is compiled as if you had written:

try {
  // non-control-flow statements A
} catch(Throwable t) {
  // non-control-flow statements B
  throw t;
}
// non-control-flow statements B

So that the statements B are executed in either case, exceptional or non-exceptional. But if you insert a control flow statement like continue into the finally block you are turning the hidden re-throw t; statement into dead code.

There is a simple way to achieve the same without a warning. Write an explicit exception handler doing the same but not containing that ineffective re-throw statement. That way you document that swallowing the exception is intentional. Then the warning will disappear. However, swallowing exceptions still remains bad code style.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top