Question

Java's checked exceptions have gotten some bad press over the years. A telling sign is that it's literally the only language in the world that has them (not even other JVM languages like Groovy and Scala). Prominent Java libraries like Spring and Hibernate also don't use them.

I personally have found one use for them (in business logic between layers), but otherwise I'm pretty anti-checked exceptions.

Are there any other uses that I don't realize?

Was it helpful?

Solution

First of all, like any other programming paradigm you need to do it right for it to work well.

For me the advantage of checked exceptions is that the authors of the Java runtime library ALREADY have decided for me what common problems I might reasonably be expected to be able to handle at the calling point (as opposed to a top-level catch-print-die block) and consider as early as possible how to handle these problems.

I like checked exceptions because they make my code more robust by forcing me to think about error recovery as early as possible.

To be more precise, to me this makes my code more robust as it forces me to consider strange corner cases very early in the process as opposed to saying "Oops, my code does not handle if the file doesn't exist yet" based on an error in production, which you then have to rework your code to handle. Adding error handling to existing code can be a non-trivial task - and hence expensive - when reaching maintenance as opposed to just doing it right from the start.

It might be that the missing file is a fatal thing and should cause the program to crash in flames, but then you make that decision with

} catch (FileNotFoundException e) {
  throw new RuntimeException("Important file not present", e);
}

This also shows a very important side effect. If you wrap an exception, you can add an explanation which goes in the stack-trace! This is so extremely powerful because you can add information about e.g. the name of the file that was missing, or the parameters passed to this method or other diagnostic information, and that information is present right in the stack trace which frequently is the single thing you get when a program has crashed.

People may say "we can just run this in the debugger to reproduce", but I have found that very frequently production errors cannot be reproduced later, and we cannot run debuggers in production except for very nasty cases where essentially your job is at stake.

The more information in your stack trace, the better. Checked exceptions help me get that information in there, and early.


EDIT: This goes for library designers as well. One library I use on a daily basis contains many, many checked exceptions which could have been designed much better making it less tedious to use.

OTHER TIPS

You've got two good answers which explain what checked exceptions have become in practice. (+1 to both.) But it would also be worthwhile to examine what they were intended for in theory, because the intention is actually worthwhile.

Checked exceptions are actually intended to make the language more type safe. Consider a simple method like integer multiplication. You might think that the result type of this method would be an integer, but, strictly speaking, the result is either an integer or an overflow exception. Considering the integer result by itself as the return type of the method does not express the full range of the function.

Seen in this light, it's not strictly true to say that checked exceptions have not found their way into other languages. They have just not taken the form that Java used. In Haskell applications, it is common to use algebraic data types to distinguish between successful and unsuccessful completion of the function. Although this is not an exception, per se, the intention is very much the same as a checked exception; it is an API designed to force the API consumer to handle both the successful in the unsuccessful case, e.g.:

data Foo a =
     Success a
   | DidNotWorkBecauseOfA
   | DidNotWorkBecauseOfB

This tells the programmer that the function has two additional possible results besides success.

IMO, checked exceptions are a flawed implementation of what might have been a pretty decent idea (under completely different circumstances -- but really isn't even a good idea under the current circumstance).

The good idea is that it would be nice to have the compiler verify that all exceptions that a program has a potential to throw will be caught. The flawed implementation is that to do that, Java forces you to deal with the exception directly in whatever class invokes anything that's declared to throw a checked exception. One of the most basic ideas (and advantages) of exception handling is that in case of an error, it allows intermediate layers of code that have nothing to do with a particular error, to completely ignore its very existence. Checked exceptions (as implemented in Java) don't allow that, thus destroying a major (the primary?) advantage of exception handling to start with.

In theory, they could have made checked exceptions useful by enforcing them only on a program-wide basis -- i.e., while "building" the program, build a tree of all the control paths in the program. Various nodes in the tree would be annotated with 1) exceptions they can generate, and 2) exceptions they can catch. To assure the program was correct, you'd then walk the tree, verifying that every exception that was thrown at any level in the tree was caught at some higher level in the tree.

The reason they didn't do that (I'd guess, anyway) is that doing so would be excruciatingly difficult -- in fact, I doubt anybody's written a build system that can do that for anything but extremely simple (bordering on "toy") languages/systems. For Java, things like dynamic class loading render it even more difficult than in many other cases. Worse, (unlike something like C) Java isn't (at least normally) statically compiled/linked to an executable. That means nothing in the system really has the global awareness to even try to do this kind of enforcement until the end user actually starts to load the program to run it.

Doing enforcement at that point is impractical for a couple of reasons. First of all, even at best it would extend startup times, which are already one of the single biggest, most common complaints about Java. Second, to do much good, you really need to detect the problem early enough for a programmer to fix it. When the user is loading the program, it's too late to do much good -- your only choices at that point are to ignore the problem, or refuse to load/run the program at all, on the off chance that there might be an exception that wasn't caught.

As they are, checked exceptions are worse than useless, but alternative designs for checked exceptions are even worse. While the basic idea for checked exceptions seems like a good one, it's really not very good, and happens to be a particularly poor fit for Java. For something like C++, that's normally compiled/linked into a complete executable with nothing much like reflection/dynamic class loading, you'd stand a little more chance (though, quite frankly, even then enforcing them correctly without the same kind of mess they currently cause in Java would still probably be beyond the current state of the art).

They're really good for annoying programmers and encouraging exception swallowing. Half the point of exceptions is so that you have a sane default way of handling errors and don't have to think about explicitly propagating error conditions you can't handle. (The sane default being that if you never handle the error anywhere in your code, you've effectively asserted that it can't happen, and are left with a sane exit and a stack trace if you're wrong.) Checked exceptions defeat this. They feel a lot like having to explicitly propagate errors in C.

I think it is fair to say that checked exceptions were a bit of a failed experiment. Where they went wrong is that they broke layering:

  • Module A might be built on top of Module B, where
  • Module B might be built on top of Module C.
  • Module C might know how to identify an error, and
  • Module A might know how to handle the error.

The nice separation of concerned is broken, because now knowledge of errors that could have been limited to A and C now have to be scattered over B.

There is a nice discussion of checked exceptions on Wikipedia.

And Bruce Eckel has a nice article on it as well. Here's a quote:

[T]he more random rules you pile onto the programmer, rules that have nothing to do with solving the problem at hand, the slower the programmer can produce. And this does not appear to be a linear factor, but an exponential one

I believe for the case of C++, if all methods have the throws specification clause, then you can have some nice optimizations. I don't believe Java could have those advantages anyway, though, because RuntimeExceptions aren't checked. A modern JIT should be able to optimize the code just fine anyway.

One nice feature of AspectJ is exception softening: You can turn checked exceptions into unchecked exceptions, specifically to enhance layering.

Perhaps it's ironic that Java went through all of this trouble, when it could have checked for null instead, which might have been much more valuable.

I love exceptions.

I am, however, constantly bemused by their miss-use.

  1. Don't use them for flow-handling. You don't want code that tries to do something and uses an exception as a trigger to do something else instead because Exceptions are objects that need to be created and use memory etc. etc. just because you couldn't be bothered to write some kind of if() check before you made your call. That's just lazy and gives the glorious Exception a bad name.

  2. Don't swallow them. Ever. Under any circumstances. As an absolute minimum you stick something in your log file to indicate that an Exception happened. Trying to track your way through code that swallows exceptions is a nightmare. Again, curse you exception-swallowers for bringing the mighty Exception into disrepute!

  3. Do treat Exceptions with your OO hat on. Create your own Exception objects with meaningful hierarchies. Layer your business logic and your exceptions hand in hand. I used to find that if I started on a new area of a system I'd find the need to sub-class Exception within half an hour of coding so these days I start by writing the Exception classes.

  4. If you're writing 'frameworky' bits of code, make sure all your initial interfaces are written to throw your own new Exceptions where appropriate... and make sure your coders have some sample code around the place of what to do when their code throws an IOException but the interface they're writing to wants to throw a 'ReportingApplicationException'.

Once you've groked how to make Exceptions work for you you'll never look back.

Checked exceptions are also in ADA.

(Warning, this post contains strongly held beliefs that you might find confronting.)

Programmers don't like them and complain, or write exception swallowing code.

Checked exceptions exist because things can not only fail to work, you can do a failure mode/effects analysis and determine this in advance.

File reads can fail. RPC calls can fail. Network IO can fail. Data can be mis-formatted when parsed.

The "happy path" for code is easy.

I knew a guy at University who could write great "happy path" code. None of the edge cases ever worked. These days he does Python for an open source company. Nuff said.

If you don't want to handle checked exceptions, what you're really saying is

While I'm writing this code, I don't want to consider obvious failure modes.

The User will just have to like the program crashing or doing weird things.

But that's okay with me because 

I'm so much more important than the people who will have to use the software

in the real, messy, error-prone world.

After all, I write the code once, you use it all day long.

So checked exceptions are not going to be liked by programmers, because it means more work.

Of course, other people might have wanted that work done.

They might have wanted the right answer even if the file server failed/ USB stick dies.

It's a strange belief in the programming community that you should be using a programming language that makes your life easier, that you enjoy, when your job is to write software. You job is solving someones problem, not letting you engage in programmatic Jazz improvisation.

If you're an amateur programmer (not programming for money), feel free to program in C# or some other language with no checked exceptions. Heck, cut out the middle man and program in Logo. You can draw pretty patterns on the floor with the turtle.

Checked exceptions should have encouraged people to handle errors close to there source. The further away from the source, the more functions have terminated in the middle of running, and the more likely that the system has entered an inconsistent state. Additionally, it is more likely that you catch the wrong exception by accident.

Sadly, people have tended to either ignore exceptions or add ever increasing throw specifications. Both of these miss the point of what checked exceptions are suppose to encourage. They are like transforming procedural code to use many Getter and Setter functions supposedly making it OO.

Essentially, checked exceptions are attempting to prove a certain degree of correctness in your code. Its pretty much the same idea as static typing, in that it attempts to prove that certain things are correct before the code even runs. The problem is that all of that extra proof takes effort and is the effort worthwhile?

Licensed under: CC-BY-SA with attribution
scroll top