Lives in the UK. Likes blogging, cycling and eating lemon drizzle cake. Roger is a DZone MVB and is not an employee of DZone and has posted 143 posts at DZone. You can read more from them at their website. View Full User Profile

Unreachable Catch Block - A Most Unobvious Bug

02.10.2012
| 6219 views |
  • submit to reddit

Earlier today I was working on some HTTP comms code adding a few changes, which are of no consequence here. One of the things that the code did was to read data from a server and, if the read failed, then it re-newed the connection and retried the read. The code ran something like this:

    HttpClient httpclient = // this is org.apache.http.client.HttpClient
    HttpResponse response;
    try {
      response = httpclient.execute(httpget, localContext);
    } catch (HttpResponseException e) {
      refreshHttpClient(httpclient);
      response = httpclient.execute(httpget, localContext);
    }

The idea is that the execute() method is supposed to throw an HttpResponseException in order to trigger a re-connection, except that the execute() method, according to the JavaDoc will NEVER throw that exception. You’d therefore expect that the compiler to give you an “Unreachable catch block” error, EXCEPT THAT IN THIS CASE IT DOESN’T. Why?

The answer is that the execute() method throws an IOException; an exception that is a superclass of the catch block exception HttpResponseException. The catch block will ignore the superclass exception as it’s only looking for the subclass exception.

To prove it, take a look at the simple example below.

public class CatchingExceptionsExample {

  public static class SampleClass {

    /**
     * Example method - throws IOException
     *
     * @throws IOException
     */
    public void method() throws IOException {

      throw new IOException("Whoops");
    }
  }

  public static class MyException extends IOException {

    private static final long serialVersionUID = 1L;

    public MyException(String msg) {
      super(msg);
    }
  }

  public static void main(String[] args) throws IOException {

    System.out.println("Running");
    try {
      SampleClass sample = new SampleClass();
      sample.method();

    } catch (MyException e) {
      System.out.println("Exception Caught");
    } finally {
      System.out.println("Ending");
    }
  }

}

Here I have an exception MyException that extends IOException and a class SampleClass that implements a sample method: method(), which throws an IOException.

The output from this code is:

Running
Ending

...demonstrating that the catch block is unreachable.

Now, my first thought was to categorise this as a bug in Java, after all you want to know when there's an unreachable catch block and the compiler / eclipse usually tells you about it with this message...


...but it isn't a bug in Java it's there intentionally and it's just one of those little Gotchas that leaps up and bites you. The reason it it intentional can be explained by looking at the following chunk of code...

public class CatchingExceptionsExample2 {

  public static class SampleClass {

    public void method() throws IOException {

      AnotherSampleClass anotherSampleClass = new AnotherSampleClass();
      anotherSampleClass.anotherMethod();
    }
  }

  public static class AnotherSampleClass {

    public void anotherMethod() throws MyException {

      throw new MyException("My Exception");
    }
  }

  public static class MyException extends IOException {

    private static final long serialVersionUID = 1L;

    public MyException(String msg) {
      super(msg);
    }
  }

  public static void main(String[] args) throws IOException {

    System.out.println("Running");
    try {
      SampleClass sample = new SampleClass();
      sample.method();

    } catch (MyException e) {
      System.out.println("Exception Caught");
    } finally {
      System.out.println("Ending");
    }
  }
}

In this code the method() method creates AnotherSampleClass and calls anotherMethod(). anotherMethod() does throw MyException, which because it's a subclass of IOException gets passed up the call-stack without any problems and is caught in the main() method. This time the output is:

Running
Exception Caught
Ending

Finally, I guess the point is that the compiler can't raise this gotcha as a problem as it'll never know if a method called in a try/catch block that then calls another method in an class or another class will throw an exception that is a subclass of the originally thrown exception... phew.

 

From http://www.captaindebug.com/2012/02/unreachable-catch-block-most-unobvious.html

Published at DZone with permission of Roger Hughes, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Ricky Clarkson replied on Fri, 2012/02/10 - 8:07am

Another surprise caused by subclassing. When will we learn?

John J. Franey replied on Fri, 2012/02/10 - 2:19pm

I'm thinking it unreasonable to expect the compiler to dip into the implementation of a called method to validate whether it indeed does throw the exception you are catching. This compile time check is pointless because of java's polymorphism and jvm's runtime class loading. Implementations other than the one compiled against can't be checked.

Also, if the main method did not declare that it throws IOException, the compiler would have given up an error like: Unhandled IOException, meaning: "all errors are not handled". This would have been a clue. You must have buried the IOException from your http sample to render this bug MOST unobvious. It would be pretty obvious if the IOException were caught and logged somewhere.

Ronald Miura replied on Fri, 2012/02/10 - 2:25pm

That seems pretty obvious to me. You can't catch an exception that is never thrown.

As for the compiler not alerting you about consequences of code external to your class, it's just not the way the Java compiler works. This kind of analysis (look deep into other classes to see if something can occur or not) takes too much time, and it is better done by third-party static analysis tools, like Findbugs (which, by the way, does a pretty decent job finding potential NullPointerExceptions and the like).

The fact that Java method binding happens only late in runtime also makes it almost impossible to predict things that are perfectly possible in, say, C++, where the compiler will just generate a big binary executable (merging all dependencies in a big blog), instead of many mostly independent pieces (Java .class files), because it knows exactly what can and what cannot change after the compilation.

Consider, for example, if the 'sample' variable was an attribute instead of a local variable. Some external logic (a DI container, for instance) could switch it to a SampleClass subclass, that throws MyException inside method(). Or, if you use AspectJ to change the method() behavior at load time.

And, you whine about the compiler not warning you, but it is indeed trying to warn you. You chose to ignore it when you declared 'throws IOException' at your 'main()' method.

No language will think in your place, just learn how to use the tool effectively.

XUEKUN KOU replied on Fri, 2012/02/10 - 8:14pm

 

I am not sure which part is surprising. In the first example, the output sequence should be try -> finally -> the throws clause of main. Where is the unreachable exception? To make sure I actually ran your code on JDK 1.6.0_30, and the output is exactly what I expected.

The second example is also pretty obvious. The compiler only checks if the body of method() can throw an exception that "is-a" IOException: in this case MyException is. At runtime, the late binding binds the raised exception to the catch block.

Roger Hughes replied on Sun, 2012/02/12 - 5:14am

All, thanks for the comments.

John, I quite agree that it's unreasonable for the compiler to be able to figure this problem out, especially if some of the dependecies are unknown until runtime. 

Ronald, I'm not whining about anything at all - I think you've got the wrong end of the stick. I'm highlighting the fact that some code I came across was expecting to catch a HttpResponseException and use that to reconnect to a server, whilst throwing superclasses of that exception. Hence the sample code...

 

  • MyException extends IOException
  • main() catches MyException, but chooses to throw IOException
  • SampleClass never throws MyException, therefore the catch block code, which may or may not be an important error handling feature of the app is never called.

 

The fact is that it is not an obvious bug. As stated above the compiler can't/shouldn't warn you of the problem because the exception (eg HttpResponseException or MyException) may be thrown from deep within the call stack, or as the result of the use of interfaces and dependency injection, or a third party API.

Ronald Miura replied on Wed, 2012/02/15 - 8:40pm in response to: Roger Hughes

Well, indeed, it's only obvious while there's 10 lines in total. In the real world it would be, like any bug in legacy code, pretty hard to find :)

Davoud Rafati replied on Sun, 2012/04/08 - 4:26am

was remarkable points

thank's 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.