Wednesday, February 3, 2010

Checked vs Unchecked: That old chestnut!

I really thought this one had gone to bed, but recently a debate on checked vs unchecked exceptions arrived at my door.

I'm associated with this Butler IO, it's basically a helper for getting and writing resources.

Does things like this:
String fromClasspath  = ButlerIO.textFrom( "res:articles/steve_jobs.txt" );

The workings of textFrom basically come from this method.
public static byte [] bytesFrom(InputStream inputStream) {
    ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
    try {
        copy(inputStream, out);
 return out.toByteArray();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

The project wasn't up a day and a complaint was received about catching the checked and re-throw a runtime exception. In their words; "... throwing an unchecked exception here is just pampering the user in false safety. Checked exceptions are there for a good reason"

This project was built to help make clean code and has followed Bruce Eckel's thoughts on this subject of exceptions.

I can't think of a time when you can recover from an IOException, and this way can identify programmer errors early and clearly at runtime. The utility doesn't enforce you to catch any exception, which makes for cleaner code...right?

Isn't the point that any error handling be the choice of the caller; Butler can't do anything about a missing file, and doesn't presume to know what the caller wants to do either.

Have I got this wrong? I don't think so! But I would appreciate some feedback from you guys. Thanks.

7 comments:

  1. If you've spent any amount of time reading source code from leading open source projects like Spring for example, you will know that wrapping checked exceptions with unchecked exceptions is generally considered to be the norm and is good practice in most cases.

    ReplyDelete
  2. I agree with everything except that it makes it quicker to identify errors. The checked exceptions do this just as quickly. In my opinion either is quick. The main issue is the tradeoff between code brevity and code transparency. As it's rare (read never) that I want to recover from an IO exception it makes sense that the code is as succinct as possible. That try catch block bloats a one liner into 5 lines (yes I'm counting the line with only brackets on). Forcing this mess onto the API client just seems like bad manners.

    ReplyDelete
  3. I agree that throwing a runtime exception is way better than a checked one as it better fits the level of abstraction ButtlerIO's API gives you.

    Imagining myself as an end user, it's pretty clear that I can expect runtime exceptions from any library that deals with external resources. I won't pampered or fooled and would plan for the worse.

    This said, I would still like to know what I'm expecting when using ButlerIO.textFrom for a missing file, as it could well be possible that it decides to return byte[0] while reserving exceptions for other types of IO issues. A little JavaDoc would do the trick here.

    ReplyDelete
  4. This example uses IOException, which in nearly all cases, should be a RuntimeException. In other cases, I might disagree, but I've always thought Java got it wrong by making an IOException a checked exception.

    If IOException was in fact a runtime exception, would you still have got the complaint?

    ReplyDelete
  5. @tan I think you're right there, if IOException was unchecked it would be the logical choice. Perhaps this is the correct thing to do here, create a custom unchecked IOException. Come to think of it, there might be value in creating a project which has unchecked versions of all java checked exceptions.

    ReplyDelete
  6. @ddossot, well said. Javadoc describing that your can expect an unchecked exception is certainly worth while as the end user. I think it's a pretty safe assumption that any IO activity can generate an unchecked exception though. If I were to create a custom RuntimeException and wrap the IOException in that, I would try and make it less generic if possible though (i.e. RuntimeFileReadException <- extends RuntimeIOException).

    ReplyDelete
  7. It seems like the general consensus here is that RuntimeException is too generic. butler-io now throws an unchecked IOException instead of a straight RuntimeException. The javadocs are also in now.

    ReplyDelete