Are checked exceptions good or bad?

Find out why some developers love checked exceptions while others hate them

1 2 Page 2
Page 2 of 2
  • A lot of pain could be eliminated by making the throws clause optional. Perhaps this is achievable through some combination of changes to the Java compiler, JVM, and classfile format.
  • The Java compiler could output warning messages whenever empty catch blocks or catch blocks that rethrow checked exceptions as unchecked exceptions are detected.

The intent of having the compiler output warning messages is to help the programmer thing about the code's design. Basically, the compiler is asking the programmer if he/she really wants to risk less-robust software. This is much friendlier to the programmer than having the compiler output error messages upon encountering empty catch blocks or checked/unchecked exception rethrows.

College programming courses that over-emphasize the one true path should evolve to pay more attention to what can go wrong and how to properly deal with failure. The Internet is quite helpful is this regard. For example, while researching this post, I encountered stackoverflow's The case against checked exceptions topic. The answer starting with I think I read the same Bruce Eckel interview that you did offers constructive advice on how to create good checked exceptions. I especially liked this item: checked exceptions when used properly should really be for things that are out of both the client programmer's and the API programmer's control. I think this is the key to not abusing checked exceptions.

Now that I've had my say, it's your turn. Feel free to contribute additional thoughts about checked exceptions and how they could be improved without sacrificing robustness in the comments.

Exceptional feedback

After posting this topic to my JavaWorld blog, I received some interesting feedback, which led to discovering a Java language feature and a cool little tool that were previously unknown to me, and which I believe you'll find useful.

Making the throws clause optional

I mentioned that "a lot of pain could be eliminated by making the throws clause optional." It turns out that there is a way to accomplish this task by taking advantage of a trick in the Java language. Check out Listing 1.

Listing 1. BypassThrowsClause.java (version 1)

import java.io.IOException;

public class BypassThrowsClause
{
   public static void main(String[] args) 
   {
      throwIOException();
   }

   private static void throwIOException()
   {
      throw helper(new IOException("I/O failure"));
   }

   private static <T extends Exception>
      RuntimeException helper(Exception e) throws T
   {
      throw (T) e;
   }
}

Listing 1 describes a small BypassThrowsClause application. This application demonstrates throwing a checked exception without requiring a throws clause or a catch handler.

The main() method invokes the throwIOException() method whose job is to throw an instance of the IOException class. Notice that this method doesn't declare a throws clause.

The body of the throwIOException() method consists of a throw statement that instantiates IOException and passes it as an argument to the helper() method during a method call. This is where the magic occurs.

helper() is a generic method whose return type is RuntimeException. This type fools the compiler into thinking that throwIOException() is receiving a RuntimeException instance (or perhaps a RuntimeException subclass instance) from helper(). Because RuntimeException is unchecked, a throws clause doesn't have to be appended to throwIOException()'s method header.

However, helper() does have a throws clause that throws a type variable (T). Also, its body consists of a throw statement that casts its argument from Exception to T and then throws the argument. Because an exception is being thrown, it's not necessary for the helper() method to return a RuntimeException (or subclass) object; the compiler doesn't complain (apart from a warning message that I'll get to shortly).

Java lets you cast to a generic type parameter. The compiler cannot check if you're casting to an inappropriate type. The JVM cannot check the cast at runtime because of type erasure. The best that can be done is for the compiler to output an "unchecked" warning message, which can be suppressed via an @SuppressWarnings("unchecked") annotation. Because of type erasure, helper()'s cast and throws clause essentially disappear and exception e is thrown.

Compile Listing 1 as follows:

javac BypassThrowsClause.java

Run the resulting application as follows:

java BypassThrowsClause

You should observe the following output:

Exception in thread "main" java.io.IOException: I/O failure
	at BypassThrowsClause.throwIOException(BypassThrowsClause.java:12)
	at BypassThrowsClause.main(BypassThrowsClause.java:7)

Suppose you want to catch the IOException via the following try-catch construct:

try
{
   throwIOException();
}
catch (IOException ioe)
{
   System.out.println("caught I/O exception");
}

If you tried to compile a modified version of Listing 1 that integrates this code fragment, the compiler would generate an error message. This message would inform you that IOException is never thrown from the try statement. The compiler is unable to detect that IOException is thrown because of the previous trick and fails to compile the source code. However, there is a solution: tell the compiler the kinds of exceptions that can be thrown via a generic method:

private static <T extends Throwable> void throws_(Class<T> clazz) throws T
{
}

This method receives the java.lang.Class object of a specific throwable and reports it via a throws clause that the compiler can check, such as in the following code fragment:

try
{
   throws_(IOException.class);
   throwIOException();
}
catch (IOException ioe)
{
   System.out.println("caught I/O exception");
}

Here, IOException.class is passed as an argument to the throws_() method. The compiler learns about this exception via that method's throws clause and the code compiles.

The throws clause hasn't been completely eliminated, as the helper() and throws_() methods attest. However, you could place these methods into a utility class as part of a library and then forget about throws.

My thanks to Johannes Brodwall for pointing out this idea to me. Also, I thank James Iry for explaining all of this via his On Removing Java Checked Exceptions By Means of Perversion blog post.

Removing checked exceptions with NoCatch

Michel Jung has developed a small (only two classes) but incredibly useful Java library for avoiding try-catch-rethrow patterns. This library is called NoCatch and is available for download on GitHub.

Consider the following traditional try-catch-rethrow Java code fragment:

try 
{
  URL url = new URL("http://www.github.com");
  // ...
} 
catch (MalformedURLException e) 
{
   throw new RuntimeException(e);
}

NoCatch lets you reduce this code fragment to the following:

import static com.github.nocatch.NoCatch.noCatch;
...
URL url = noCatch(() -> new URL("http://www.github.com"));

NoCatch's documentation also illustrates custom wrapper exceptions and global custom wrapper exceptions. You'll never again have to catch checked exceptions only to rethrow them as unchecked exceptions.

download
Get the source code for this post's applications. Created by Jeff Friesen for JavaWorld

The following software was used to develop the post's code:

  • 64-bit JDK 8u60

The post's code was tested on the following platform(s):

  • JVM on 64-bit Windows 8.1
1 2 Page 2
Page 2 of 2