Thursday, 2 February 2012

Unreachable Catch Block - A Most Unobvious Bug

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 is 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.

5 comments:

tcmaster said...

I guess IDEA is clever enough to detect this kind of error.

Ben Rowland said...

This behaviour seems clearer if you imagine an interface method throwing a superclass exception type. The compiler cannot know which implementations will be around at runtime, and which subclass flavours of exception they might throw.

So client code which invokes the method on an interface type might catch subclass exception types which never could be thrown at runtime.

I've not tested this theory, though!

Ricky Clarkson said...

Another surprise caused by subclassing. When will we learn?

Mad Prophet said...

Why do you throw IOException from the main method. Remove it and see what happens.

--
ilx

Roger Hughes said...

Mad Prophet, thanks for the comment. The code shown is just sample code used to demonstrate the point that it's possible to add an exception handlers to your code that are never called and will not be flagged as unreachable catch blocks because the exceptions they catch are sub classes of exceptions that are thrown by the code and that it's possible to write code that relies on this unreachable code.

As for the IOException in main(), that's there just to make the code compile, if it was removed, you'd have an unhanded exception compilation.