Running on Java 24-ea+21-2447 (Preview)
Home of The JavaSpecialists' Newsletter

033Making Exceptions Unchecked

Author: Dr. Heinz M. KabutzDate: 2001-10-18Java Version: 1.3Category: Exceptions
 

Abstract: Checked exceptions are a strange design that can make Java frustrating to use. In this article we wrap it with an unchecked exception, but delegate the printing methods to the wrapped exception.

 

Welcome to the 33rd issue of The Java(tm) Specialists' Newsletter, which some cynics say should be renamed to "The Java Hackers' Newsletter". Yes, ok, that would probably be a more appropriate name considering some of our articles, but I just find that examining some of the weird behaviour in Java helps me to understand the language better.

Thanks to all of you who responded to my last survey where I asked you to send me email if you could *not* read my newsletter. You remind me of my good friend at university who won ALL the class medals for Computer Science. I once managed to beat him in a subject (parallel computing) where I got 90+% and he got 65%. He had thought he should answer 2 out of 4 questions, whereas he should have answered 3 out of 4 questions! The problem was that he did not read the question ;-)

javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

Making Exceptions Unchecked

Why Checked Exceptions Cause Bugs

How often have you seen code such as this?

  // ...
  try {
    // ... some code that could throw IOException or RemoteException
  } catch(Exception ex) {} // ignore

Or even worse, how about the following (from a real program whose author shall remain anonymous):

  Connection con;
  try {
    // ... make a connection
  } catch(Throwable t) {
    con = null;
  }

You can imagine what happened. At some point, the piece of code that needed to make the connection tried to load a class that was not in the classpath, with a resulting NoClassDefFoundError. The program stopped working without informing us of what had gone wrong. After a while I noticed that the class could not have been included in the classpath and was horrified to find the above code.

Why is it that so many novice Java developers write code that catches Exception and then keeps quiet about it? Is it because the compiler forces us to catch them - and most beginners then don't know how to handle them correctly? I personally like checked exceptions because they tell me what things could go wrong in my environment, although not always very clearly. For example, java.io.IOException has 47 subclasses in JDK 1.3.1, so if a method throws IOException you really don't know what actually went wrong.

Sometimes I want to catch several checked exceptions and then do something about them as a group. The problem with catching Exception is that you then also catch the unchecked exceptions, i.e. derivatives of java.lang.RuntimeException, such as NullPointerException. I like to call the unchecked exceptions programmer bug exceptions and in general you would not want to swallow those exceptions. In that case I normally do the following:

  // ...
  try {
    // ... some code that could throw IOException or RemoteException
  } catch(RuntimeException ex) {
    // programmer bug - rethrow
    throw ex;
  } catch(Exception ex) {
    // handle all the exceptions correctly
  }

Incorrect handling of exceptions is the cause of most of the bugs I have seen in novice Java programmers' code. Seeing that most Java programmers are novices due to the age of the language, we could say that, even though I like them, checked exceptions could perhaps have been a mistake. C++ doesn't have them, neither does C# and also not Python. (In case you think I'm a great visionary for thinking that they might be a mistake, I got that idea from someone else ;-)

Wrapping Checked Exceptions in Unchecked Exceptions

So how do you convert a checked exception into an unchecked exception? Here we have a code sample of how one could write an Object Adapter that would convert a checked exception into a RuntimeException. (If you don't know the difference between an object and a class adapter, and what the applicability and the consequences of each are, you should seriously consider coming to my design patterns course - see beginning of newsletter for more information.)

One of the challenges of adapting exceptions is that you want to capture the exact place where the error occurred in the program, rather than the place where the checked exception was caught. The following would therefore not be the right way to do it, as you would have an incorrect line number to blame for the fault:

  // incorrect way of adapting a checked exception
  try {
    // ... some code that could throw IOException or RemoteException
  } catch(Exception ex) {
    throw new RuntimeException(ex.toString());
  }

After some experiments, I found that the ExceptionConverter shown below works fairly well.

/**
The ExceptionConverter changes a checked exception into an
unchecked exception.
*/
public class ExceptionConverter extends RuntimeException {
  /** we keep a handle to the wrapped exception */
  private final Exception ex;
  public ExceptionConverter(Exception ex) {
    this.ex = ex;
  }
  /** and allow the user of ExceptionConverter to get a handle to it. */
  public Exception getException() {
    return ex;
  }
  /** We print the message of the checked exception */
  public String getMessage() {
    return ex.getMessage();
  }
  /** and make sure we also produce a localized version */
  public String getLocalizedMessage() {
    return ex.getLocalizedMessage();
  }
  /** The toString() is changed to be prefixed with ExceptionConverter */
  public String toString() {
    return "ExceptionConverter: " + ex;
  }
  /** we have to override this as well */
  public void printStackTrace() {
    printStackTrace(System.err);
  }
  /** here we prefix, with s.print(), not s.println(), the stack
    trace with "ExceptionConverter:" */
  public void printStackTrace(java.io.PrintStream s) {
    synchronized (s) {
      s.print("ExceptionConverter: ");
      ex.printStackTrace(s);
    }
  }
  /** Again, we prefix the stack trace with "ExceptionConverter:" */
  public void printStackTrace(java.io.PrintWriter s) {
    synchronized (s) {
      s.print("ExceptionConverter: ");
      ex.printStackTrace(s);
    }
  }
  /** requests to fill in the stack trace we will have to ignore
  (I think)  We can't throw an exception here, as this method
  is called by the constructor of Throwable */
  public Throwable fillInStackTrace() {
    return this;
  }
}

We can try this out by throwing some exceptions around:

import java.io.IOException;
public class ExceptionConverterTest {
  private static void f() throws IOException {
    throw new IOException("File broken");
  }
  private static void g() {
    try {
      f();
    } catch(IOException ex) {
      System.out.println("Printing out plain ol' IOException:");
      System.out.println("---");
      ex.printStackTrace();
      System.out.println("---");
      throw new ExceptionConverter(ex);
    }
  }
  public static void main(String args[]) {
    try {
      g();
    } catch(RuntimeException ex) {
      System.out.println("Printing out RuntimeException:");
      System.out.println("---");
      ex.printStackTrace();
      System.out.println("---");
      System.out.println("That's it!");
    }
  }
}

The resulting output is:

Printing out plain ol' IOException:
---
java.io.IOException: File broken
        at ExceptionConverterTest.f(ExceptionConverterTest.java:5)
        at ExceptionConverterTest.g(ExceptionConverterTest.java:9)
        at ExceptionConverterTest.main(ExceptionConverterTest.java:20)
---
Printing out RuntimeException:
---
ExceptionConverter: java.io.IOException: File broken
        at ExceptionConverterTest.f(ExceptionConverterTest.java:5)
        at ExceptionConverterTest.g(ExceptionConverterTest.java:9)
        at ExceptionConverterTest.main(ExceptionConverterTest.java:20)
---
That's it!

As you can see, the line numbers for the two exceptions are the same, pointing us to the exact source of our troubles, without forcing us to use checked exceptions.

Is it a good idea to adapt checked exceptions into unchecked ones? In general I would say "no". However, there are some cases where you are overriding a method which does not throw a checked exception that you need to throw and in those cases it can sometimes be useful. How often have I morphed exceptions? Not often.

I'm looking forward to your comments on this newsletter.

Cheersio

Heinz


Errata

In my last newsletter I made a silly mistake - I forgot to set instance back to null. The make() method of the CleverLicenseManager class should have been:

  public static CleverLicenseManager make() {
    synchronized(CleverLicenseManager.class) {
      try {
        new CleverLicenseManager();
      } catch(Exception ex) {} // ignore
      try {
        while (instance == null) {
          System.gc();
          CleverLicenseManager.class.wait(100);
        }
        return instance;
      } catch(InterruptedException ex) {
        return null;
      } finally {
        instance = null;
      }
    }
  }
 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...