For a number of years now I have been unable to get a decent answer to the following question: why are some developers so against checked exceptions? I have had numerous conversations, read things on blogs, read what Bruce Eckel had to say (the first person I saw speak out against them).
I am currently writing some new code and paying very careful attention to how I deal with exceptions. I am trying to see the point of view of the "we don't like checked exceptions" crowd and I still cannot see it.
Every conversation I have ends with the same question going unanswered... let me set it up:
In general (from how Java was designed),
- Error is for things that should never be caught (VM has a peanut allergy and someone dropped a jar of peanuts on it)
- RuntimeException is for things that the programmer did wrong (programmer walked off the end of an array)
- Exception (except RuntimeException) is for things that are out of the programmer's control (disk fills up while writing to the file system, file handle limit for the process has been reached and you cannot open any more files)
- Throwable is simply the parent of all of the exception types.
A common argument I hear is that if an exception happens then all the developer is going to do is exit the program.
Another common argument I hear is that checked exceptions make it harder to refactor code.
For the "all I am going to do is exit" argument I say that even if you are exiting you need to display a reasonable error message. If you are just punting on handling errors then your users won't be overly happy when the program exits without a clear indication of why.
For the "it makes it hard to refactor" crowd, that indicates that the proper level of abstraction wasn't chosen. Rather than declare a method throws an IOException, the IOException should be transformed into an exception that is more suited for what is going on.
I don't have an issue with wrapping Main with catch(Exception) (or in some cases catch(Throwable) to ensure that the program can exit gracefully - but I always catch the specific exceptions I need to. Doing that allows me to, at the very least, display an appropriate error message.
The question that people never reply to is this:
If you throw RuntimeException subclasses instead of Exception subclasses then how do you know what you are supposed to catch?
If the answer is catch Exception then you are also dealing with programmer errors the same way as system exceptions. That seems wrong to me.
If you catch Throwable then you are treating system exceptions and VM errors (and the like) the same way. That seems wrong to me.
If the answer is that you catch only the exceptions you know are thrown then how do you know what ones are thrown? What happens when programmer X throws a new exception and forgot to catch it? That seems very dangerous to me.
I would say that a program that displays a stack trace is wrong. Do people who don't like checked exceptions not feel that way?
So, if you don't like checked exceptions can you explain why not AND answer the question that doesn't get answered please?
Edit: I am not looking for advice on when to use either model, what I am looking for is why people extend from RuntimeException because they don't like extending from Exception and/or why they catch an exception and then rethrow a RuntimeException rather than add throws to their method. I want to understand the motivation for disliking checked exceptions.
Exception categories
When talking about exceptions I always refer back to Eric Lippert's Vexing exceptions blog article. He places exceptions into these categories:
OutOfMemoryError
orThreadAbortException
.ArrayIndexOutOfBoundsException
,NullPointerException
or anyIllegalArgumentException
.NumberFormatException
fromInteger.parseInt
instead of providing anInteger.tryParseInt
method that returns a boolean false on parse failure.FileNotFoundException
.An API user:
Checked exceptions
The fact that the API user must handle a particular exception is part of the method's contract between the caller and the callee. The contract specifies, among other things: the number and types of arguments the callee expects, the type of return value the caller can expect, and the exceptions the caller is expected to handle.
Since vexing exceptions should not exist in an API, only these exogenous exceptions must be checked exceptions to be part of the method's contract. Relatively few exceptions are exogenous, so any API should have relatively few checked exceptions.
A checked exception is an exception that must be handled. Handling an exception can be as simple as swallowing it. There! The exception is handled. Period. If the developer wants to handle it that way, fine. But he can't ignore the exception, and has been warned.
API problems
But any API that has checked vexing and fatal exceptions (e.g. the JCL) will put unnecessary strain on the API users. Such exceptions have to be handled, but either the exception is so common that it should not have been an exception in the first place, or nothing can be done when handling it. And this causes Java developers to hate checked exceptions.
Also, many APIs don't have a proper exception class hierarchy, causing all kinds of non-exogenous exception causes to be represented by a single checked exception class (e.g.
IOException
). And this also causes Java developers to hate checked exceptions.Conclusion
Exogenous exceptions are those that are not your fault, could not have been prevented, and which should be handled. These form a small subset of all the exceptions that can get thrown. APIs should only have checked exogenous exceptions, and all other exceptions unchecked. This will make better APIs, put less strain on the API user, and therefore reduce the need to catch all, swallow or rethrow unchecked exceptions.
So don't hate Java and its checked exceptions. Instead, hate the APIs that overuse checked exceptions.
This isn't an argument against the pure concept of checked exceptions, but the class hierarchy Java uses for them is a freak show. We always call the things simply "exceptions" – which is correct, because the language specification calls them that too – but how is an exception named and represented in the type system?
By the class
Exception
one imagines? Well no, becauseException
s are exceptions, and likewise exceptions areException
s, except for those exceptions that are notException
s, because other exceptions are actuallyError
s, which are the other kind of exception, a kind of extra-exceptional exception that should never happen except when it does, and which you should never catch except sometimes you have to. Except that's not all because you can also define other exceptions that are neitherException
s norError
s but merelyThrowable
exceptions.Which of these are the "checked" exceptions?
Throwable
s are checked exceptions, except if they're alsoError
s, which are unchecked exceptions, and then there's theException
s, which are alsoThrowable
s and are the main type of checked exception, except there's one exception to that too, which is that if they are alsoRuntimeException
s, because that's the other kind of unchecked exception.What are
RuntimeException
s for? Well just like the name implies, they're exceptions, like allException
s, and they happen at run-time, like all exceptions actually, except thatRuntimeException
s are exceptional compared to other run-timeException
s because they aren't supposed to happen except when you make some silly error, althoughRuntimeException
s are neverError
s, so they're for things that are exceptionally erroneous but which aren't actuallyError
s. Except forRuntimeErrorException
, which really is aRuntimeException
forError
s. But aren't all exceptions supposed to represent erroneous circumstances anyway? Yes, all of them. Except forThreadDeath
, an exceptionally unexceptional exception, as the documentation explains that it's a "normal occurrence" and that that's why they made it a type ofError
.Anyway, since we're dividing all exceptions down the middle into
Error
s (which are for exceptional execution exceptions, so unchecked) andException
s (which are for less exceptional execution errors, so checked except when they're not), we now need two different kinds of each of several exceptions. So we needIllegalAccessError
andIllegalAccessException
, andInstantiationError
andInstantiationException
, andNoSuchFieldError
andNoSuchFieldException
, andNoSuchMethodError
andNoSuchMethodException
, andZipError
andZipException
.Except that even when an exception is checked, there are always (fairly easy) ways to cheat the compiler and throw it without it being checked. If you do you that you may get an
UndeclaredThrowableException
, except in other cases, where it could throw up as anUnexpectedException
, or anUnknownException
(which is unrelated toUnknownError
, which is only for "serious exceptions"), or anExecutionException
, or anInvocationTargetException
, or anExceptionInInitializerError
.Oh, and we mustn't forget Java 8's snazzy new
UncheckedIOException
, which is aRuntimeException
exception designed to let you throw the exception checking concept out the window by wrapping checkedIOException
exceptions caused by I/O errors (which don't causeIOError
exceptions, although that exists too) that are exceptionally difficult to handle and so you need them to not be checked.Thanks Java!
We've seen some references to C#'s chief architect.
Here's an alternate point of view from a Java guy about when to use checked exceptions. He acknowledges many of the negatives others have mentioned: Effective Exceptions
Rather than rehash all the (many) reasons against checked exceptions, I'll pick just one. I've lost count of the number of times I've written this block of code:
99% of the time I can't do anything about it. Finally blocks do any necessary cleanup (or at least they should).
I've also lost count of the number of times I've seen this:
Why? Because someone had to deal with it and was lazy. Was it wrong? Sure. Does it happen? Absolutely. What if this were an unchecked exception instead? The app would've just died (which is preferable to swallowing an exception).
And then we have infuriating code that uses exceptions as a form of flow control, like java.text.Format does. Bzzzt. Wrong. A user putting "abc" into a number field on a form is not an exception.
Ok, i guess that was three reasons.
I know this is an old question but I've spent a while wrestling with checked exceptions and I've something to add. Please forgive me for the length of it!
My main beef with checked exceptions is that they ruin polymorphism. It's impossible to make them play nicely with polymorphic interfaces.
Take the good ol' Java
List
interface. We have common in-memory implementations likeArrayList
andLinkedList
. We also have the the skeletal classAbstractList
which makes it easy to design new types of list. For a read-only list we need to implement only two methods:size()
andget(int index)
.This example
WidgetList
class reads some fixed-size objects of typeWidget
(not shown) from a file:By exposing the Widgets using the familiar
List
interface, you can retrieve items (list.get(123)
) or iterate a list (for (Widget w : list) ...
) without needing to know aboutWidgetList
itself. One can pass this list to any standard methods that use generic lists, or wrap it in aCollections.synchronizedList
. Code that uses it need neither know nor care whether the "Widgets" are made up on the spot, come from an array, or are read from a file, or a database, or from across the network, or from a future subspace relay. It will still work correctly because theList
interface is correctly implemented.Except it isn't. The above class doesn't compile because the file access methods may throw an
IOException
, a checked exception which you have to "catch or specify". You can't specify it as thrown -- the compiler won't let you because that would violate the contract of theList
interface. And there is no useful way thatWidgetList
itself can handle the exception (as I'll expound on later).Apparently the only thing to do is catch and rethrow checked exceptions as some unchecked exception:
((Edit: Java 8 has added an
UncheckedIOException
class for exactly this case: for catching and rethrowingIOException
s across polymorphic method boundaries. Kind of proves my point!))So checked exceptions simply don't work in cases like this. You can't throw them. Ditto for a clever
Map
backed by a database, or an implementation ofjava.util.Random
connected to a quantum entropy source via a COM port. As soon as you try to do anything novel with the implementation of a polymorphic interface, the concept of checked exceptions fails. But checked exceptions are so insidious that they still won't leave you in peace, because you still have to catch and rethrow any from lower-level methods, cluttering the code and cluttering the stack trace.I find that the ubiquitous
Runnable
interface is often backed into this corner, if it calls something which throws checked exceptions. It can't throw the exception as is, so all it can do is clutter the code by catching and rethrowing as aRuntimeException
.Actually, you can throw undeclared checked exceptions if you resort to hacks. The JVM, at run time, doesn't care about checked exception rules, so we need to fool only the compiler. The easiest way to do this is to abuse generics. This is my method for it (class name shown because (before Java 8) it's required in the calling syntax for the generic method):
Hurray! Using this we can throw a checked exception any depth up the stack without declaring it, without wrapping it in a
RuntimeException
, and without cluttering the stack trace! Using the "WidgetList" example again:Unfortunately, the final insult of checked exceptions is that the compiler refuses to allow you to catch a checked exception if, in its flawed opinion, it could not have been thrown. (Unchecked exceptions do not have this rule.) To catch the sneakily thrown exception we have to do this:
That is a bit awkward, but on the plus side, it is still slightly simpler than the code for extracting a checked exception that was wrapped in a
RuntimeException
.Happily, the
throw t;
statement is legal here, even though the type oft
is checked, thanks to a rule added in Java 7 about rethrowing caught exceptions.When checked exceptions meet polymorphism, the opposite case is also a problem: when a method is spec'd as potentially throwing a checked exception, but an overridden implementation doesn't. For example, the abstract class
OutputStream
'swrite
methods all specifythrows IOException
.ByteArrayOutputStream
is a subclass that writes to an in-memory array instead of a true I/O source. Its overriddenwrite
methods cannot causeIOException
s, so they have nothrows
clause, and you can call them without worrying about the catch-or-specify requirement.Except not always. Suppose that
Widget
has a method for saving it out to a stream:Declaring this method to accept a plain
OutputStream
is the right thing to do, so it can be used polymorphically with all kinds of outputs: files, databases, the network, and so on. And in-memory arrays. With an in-memory array, however, there is a spurious requirement to handle an exception that can't actually happen:As usual, checked exceptions get in the way. If your variables are declared as a base type that has more open-ended exception requirements, you have to add handlers for those exceptions even if you know they won't occur in your application.
But wait, checked exceptions are actually so annoying, that they won't even let you do the reverse! Imagine you currently catch any
IOException
thrown bywrite
calls on anOutputStream
, but you want to change the variable's declared type to aByteArrayOutputStream
, the compiler will berate you for trying to catch a checked exception that it says cannot be thrown.That rule causes some absurd problems. For example, one of the three
write
methods ofOutputStream
is not overridden byByteArrayOutputStream
. Specifically,write(byte[] data)
is a convenience method that writes the full array by callingwrite(byte[] data, int offset, int length)
with an offset of 0 and the length of the array.ByteArrayOutputStream
overrides the three-argument method but inherits the one-argument convenience method as-is. The inherited method does exactly the right thing, but it includes an unwantedthrows
clause. That was perhaps an oversight in the design ofByteArrayOutputStream
, but they can never fix it because it would break source compatibility with any code that does catch the exception -- the exception that has never, is never, and never will be thrown!That rule is annoying during editing and debugging too. E.g., sometimes I'll comment out a method call temporarily, and if it could have thrown a checked exception, the compiler will now complain about the existence of the local
try
andcatch
blocks. So I have to comment those out too, and now when editing the code within, the IDE will indent to the wrong level because the{
and}
are commented out. Gah! It's a small complaint but it seems like the only thing checked exceptions ever do is cause trouble.I'm nearly done. My final frustration with checked exceptions is that at most call sites, there's nothing useful you can do with them. Ideally when something goes wrong we'd have a competent application-specific handler that can inform the user of the problem and/or end or retry the operation as appropriate. Only a handler high up the stack can do this because it's the only one that knows the overall goal.
Instead we get the following idiom, which is rampant as a way to shut the compiler up:
In a GUI or automated program the printed message won't be seen. Worse, it plows on with the rest of the code after the exception. Is the exception not actually an error? Then don't print it. Otherwise, something else is going to blow up in a moment, by which time the original exception object will be gone. This idiom is no better than BASIC's
On Error Resume Next
or PHP'serror_reporting(0);
.Calling some kind of logger class is not much better:
That is just as lazy as
e.printStackTrace();
and still plows on with code in an indeterminate state. Plus, the choice of a particular logging system or other handler is application-specific, so this hurts code reuse.But wait! There is an easy and universal way to find the application-specific handler. It's higher up the call stack (or it is set as the Thread's uncaught exception handler). So in most places, all you need to do is throw the exception higher up the stack. E.g.,
throw e;
. Checked exceptions just get in the way.I'm sure checked exceptions sounded like a good idea when the language was designed, but in practice I've found them to be all bother and no benefit.
I think that this is an excellent question and not at all argumentative. I think that 3rd party libraries should (in general) throw unchecked exceptions. This means that you can isolate your dependencies on the library (i.e. you don't have to either re-throw their exceptions or throw
Exception
- usually bad practice). Spring's DAO layer is an excellent example of this.On the other hand, exceptions from the core Java API should in general be checked if they could ever be handled. Take
FileNotFoundException
or (my favourite)InterruptedException
. These conditions should almost always be handled specifically (i.e. your reaction to anInterruptedException
is not the same as your reaction to anIllegalArgumentException
). The fact that your exceptions are checked forces developers to think about whether a condition is handle-able or not. (That said, I've rarely seenInterruptedException
handled properly!)One more thing - a
RuntimeException
is not always "where a developer got something wrong". An illegal argument exception is thrown when you try and create anenum
usingvalueOf
and there's noenum
of that name. This is not necessarily a mistake by the developer!