Once upon a time Java created an experiment called checked-exceptions, you know you have to declare exceptions or catch them. Since that time no other language (I know of) has decided to copy this idea, but somehow the Java developers are in love with checked exceptions. Here, I am going to “try” to convince you that checked-exceptions, even thought look like a good idea at first glance, are actually not a good idea at all:
Empirical Evidence
Let’s start with an observation of your code base. Look through your code and tell me what percentage of catch blocks do rethrow or print error? My guess is that it is in high 90s. I would go as far as 98% of catch blocks are meaningless, since they just print an error or rethrow the exception which will later be printed as an error. The reason for this is very simple. Most exceptions such as FileNotFoundException, IOException, and so on are sign that we as developers have missed a corner case. The exceptions are used as away of informing us that we, as developers, have messed up. So if we did not have checked exceptions, the exception would be throw and the main method would print it and we would be done with it (optionally we would catch in main all exceptions and log them if we are a server).
Checked exceptions force me to write catch blocks which are meaningless: more code, harder to read, and higher chance that I will mess up the rethrow logic and eat the exception.
Lost in Noise
Now lets look at the 2-5% of the catch blocks which are not rethrow and real interesting logic happens there. Those interesting bits of useful and important information is lost in the noise, since my eye has been trained to skim over the catch blocks. I would much rather have code where a catch would indicate, pay, attention here something interesting happens here, rather than, it is just a rethrow. Now, if we did not have checked exceptions, you would write your code without catch, test your code (you do test right?) and realize that under these circumstances an exception is throw and deal with it. In such a case forgetting to write a catch block is no different than forgetting to write an else block of the if statement. We don’t have checked ifs and yet no one misses them, so why do we need to tell developers that FileNotFound can happen. What if the developer knows for a fact that it can not happen since he has just placed the file there, and so such an exception would mean that your filesystem has just disappeared and your application is not place to handle that.
Checked exception make me skim the catch as most are just rethrows, making it likely that I will miss something important.
Unreachable Code
I love to write tests first and implement as a consequence of tests. In such a situation you should always have 100% coverage since you are only writing what the tests are asking for. But you don’t! It is less than 100% because checked exceptions force you to write catch blocks which are impossible to execute. Check this code out:
bytesToString(byte[] bytes) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { out.write(bytes); out.close() return out.toSring(); } catch (IOException e) { // This can never happen! // Should I rethrow? Eat it? Print Error? } }
ByteArrayOutputStream will never throw IOException! You can look through its implementation and see that it is true! So why are you making me catch a phantom exception which can never happen and which I can not write a test for? As a result I cannot claim 100% coverage because of things outside my control.
Checked exceptions create dead code which will never execute.
Closures Don’t Like You
Java does not have closures but it has visitor pattern. Let me explain with concrete example. I was creating a custom class loader and need to override load() method on MyClassLoader which throws ClassNotFoundException under some circumstances. I use ASM library which allows me to inspect Java bytecodes. The way ASM works is that it is a visitor pattern, I write visitors and as ASM parses the bytecodes it calls specific methods on my visitor implementation. One of my visitors as it is examining bytcodes decides that things are not right and needs to throw a ClassNotFondException which the class loader contract says it should throw. But now we have a problem. What we have on a stack is MyClassLoader -> ASMLibrary -> MyVisitor. MyVisitor wants to throw an exception which MyClassLoader expects but it can not since ClassNotFoundException is checked and ASMLibrary does not declare it (nor should it). So I have to throw RuntimeClassNotFoundException from MyVisitor which can pass through ASMLibrary which MyClassLoader can catch and rethrow as ClassNotFoundException.
Checked exception get in the way of functional programing.
Lost Fidelity
Suppose java.sql package would be implemented with useful exception such as SqlDuplicateKeyExceptions and SqlForeignKeyViolationException and so on (we can wish) and suppose these exceptions are checked (which they are). We say that the SQL package has high fidelity of exception since each exception is to a very specific problem. Now lets say we have the same set up as before where there is some other layer between us and the SQL package, that layer can either redeclare all of the exceptions, or more likely throw its own. Let’s look at an example, Hibernate is object-relational-database-mapper, which means it converts your SQL rows into java objects. So on the stack you have MyApplication -> Hibernate -> SQL. Here Hibernate is trying hard to hide the fact that you are talking to SQL so it throws HibernateExceptions instead of SQLExceptions. And here lies the problem. Your code knows that there is SQL under Hibernate and so it could have handled SqlDuplicateException in some useful way, such as showing an error to the user, but Hibernate was forced to catch the exception and rethrow it as generic HibernateException. We have gone from high fidelity SqlException to low fidelity HibernateException. An so MyApplication can not do anything. Now Hibernate could have throw HibernateDuplicateKeyException but that means that Hibernate now has the same exception hierarchy as SQL and we are duplicating effort and repeating ourselves.
Rethrowing checked exceptions causes you to lose fidelity and hence makes it less likely that you could do something useful with the exception later on.
You can’t do Anything Anyway
In most cases when exception is throw there is no recovery. We show a generic error to the user and log an exception so that we can file a bug and make sure that that exception will not happen again. Since 90+% of the exception are bugs in our code and all we do is log, why are we forced to rethrow it over and over again.
It is rare that anything useful can be done when checked exception happens, in most case we die with error, so make that the default behavior of my code with no additional typing.
How I deal with the code
Here is my strategy to deal with java:
- Always catch all checked exceptions at source and rethrow them as LogRuntimeException.
- My runtime un-checked exception which says I don’t care just log it.
- Here I have lost Exception fidelity.
- All of my methods do not declare any exceptions
- As I discover that I need to deal with a specific exception I go back to the source where LogRuntimeException was thrown and I change it to <Specific>RuntimeException (This is rarer than you think)
- I am restoring the exception fidelity only where needed.
- Net effect is that when you come across a try-catch clause you better pay attention as interesting things are happening there.
- Very few try-catch calluses, code is much easier to read.th
- Very close to 100% test coverage as there is no dead code in my catch blocks.
20 responses so far ↓
Good writing as always, Misko. I think this article is less convincing than it could be, and here’s why.
No developer should ever catch, log, and re-throw an exception. “Never” is only slightly too strong. Only catch an exception if you intend to (a) handle it, (b) transform it, or (c) silence it. The exception should end at the point it’s logged except in rare circumstances IMO.
Also, Hibernate would transform SQL exceptions even if they were unchecked. Why? Because it defines a contract with the developer. It says to us: “Use me in this way, and be prepared to handle these exceptions, and your application will be well-behaved *regardless of the underlying data store*.”
The loss of fidelty in this case (and many other cases) is a symptom of the contract trying to abstract away lower level implementation details. Maybe it shouldn’t do that–and it could follow this advice even now.
What I always liked about checked exceptions was the auto-documentation and hints to the compiler. If I am calling a method with checked exceptions, I know which corner cases to be concerned about, and I know the name of the exception that’s thrown in those cases.
By contrast, if I am calling a method with unchecked exceptions (as in most languages), I don’t know any of that. I’m at the mercy of the developer to document what might happen, and we all know documentation is perpetually out of date. Moreover, there’s no hint what exceptions will be thrown from methods that *it* calls.
Sadly, I have also been convinced that checked exceptions are bad, and have been converting to unchecked exceptions in Java code for a couple years now. But I still miss the “good old days” when I could count on the compiler to assist me in my efforts to write solid code.
I agree on the pain of catching checked exception and I almost never use them. But since the purpose of Hibernate is to decouple the application from the database, wouldn’t it be strange to catch exception from that layer?
I never understood this fascination with checked exceptions. If they’re supposed to help the programmer with hints – well, the IDE already has access to the source code, so it can do that. Why is the programmer supposed to do a lot of repetitive work in order to make the compiler happy? That’s ass-backwards. There’s already too much ceremony in Java, adding to that does not strike me as a good idea.
Every time I used checked exception I fell pain in every level, when transporting those exceptions to upper levels. Thanks for explanation!
I have to agree with Giorgio. Allowing SQL exceptions to pass through Hibernate would destroy the abstractions that Hibernate provides. Your code *doesn’t* (or shouldn’t) know that there’s SQL under Hibernate. Despite the duplication, it seems to me that Hibernate should define new exceptions that abstract on exceptions from the SQL layer. In this case there is a lot of duplication since Hibernate is so close to the actual database – if Hibernate abstracted the database more, in some way, the exceptions it would define would appear less redundant.
I don’t understand why you are catching checked exceptions and rethrowing them. Why not add the throws to the method declaration? I know that can be a pain as well, but at least it keeps your try/catch code correct.
Just the other day I was asked at work “what is the best practice regarding when to throw a checked exception vs. an unchecked one?”
I had to explain that such guidelines (I think Sun once recommended that you should “use unchecked only for things you can’t anticipate”) are nothing more than a legend we tell our young programmers before they go to sleep.
Miško, why did it take more than 13 years for you to realize?
This conclusions and approaches were known by engineers for decades. For example, look at the following article published about a year before the release of Java 1.1:
http://www.bleading-edge.com/Publications/C++Report/v9607/Column2.htm
@Alexander,
I guess I am slow.
What theoretically seemed a good idea at the time, for the reasons that you explain here, in practice it’s not a good idea at all. I don’t even remember when was the last time that I have created a checked exception.
As Bob Martin in his last book says: The debate is over! guys let close this debate.
Few people know the Fault-Contingency exception hangling scheme by Barry Ruzek. Not perfect, though, it provides some useful vocabulary to talk about exceptions.
There was interresting discussion on that topic already:
http://www.theserverside.com/news/thread.tss?thread_id=43820
The over-simplified summary:
1. put a fault-bareer early in the call tree, log all errors or display them to the user there
2. use unchecked exceptions almost everywhere
3. use checked exceptions in case of Contingencies – where “Contingency – An expected condition demanding an alternative response from a method that can be expressed in terms of the method’s intended purpose. The caller of the method expects these kinds of conditions and has a strategy for coping with them.”
This way, you have your app secured (by fault-bareers), unnecessary exceptions don’t pollute your code and compiler helps you when you want to.
Clearly, some problems with checked exceptions remain (when you use functional style for example), but Barry’s rules help in most cases – I can recommend them.
I more agree with Michael than with others.
I think checked exceptions help writing solid code.
Even if I have to re-throw, I add usual context info.
Example: NullPointerException. It is useless to see NPE without details on data that caused it.
If NPE were checked, programmers would at least log it along with related data. It would be much easier to report and reproduce it. And all NPE would gone
Since many love unchecked exceptions, world is filled with brittle programs working only the way they were tested.
100% coverage of my module doesn’t help. Because my module misses 50% of cases when underlying libraries return/throw something unusual.
It is known as defects of commission and defects of omission. Coverage helps with commission. Checked exceptions/contracts help with omission.
I agree I often write fluky programs. It would be useful to switch checked exceptions off for such projects. (For example, see http://projectlombok.org/disableCheckedExceptions.html)
But sometimes I write server-side production code. I would make all exceptions checked there. And I would use only libraries that compile in all-exceptions-checked mode.
Blog - all about IT » O testach wydajnościowych słów kilka // Nov 6, 2009 at 7:18 am
[...] wydajnościowe, jak to testy… robić trzeba. Jak to pisze Misko: You do test, right? Zazwyczaj aby je przeprowadzić korzystałem z Apache JMeter. Polecam ten program i może nawet [...]
Checked exceptions within the Java API should be removed. They could do it by moving them all extend RuntimeExceptions.
IMO Checked exceptions belong in the domain only.
It sounds like you aren’t entirely against catching exceptions. That is what I don’t understand, is it OK to catch some exceptions or just throw and rethrow everything to the highest level in your application?
It just seems that if you do that, then your code will always have problems and errors.
At least with catching exceptions like FileNotFoundException and dead code, the application is able to continue.
Misko, I love your work and your post is quite convincing. But seriously, you NEVER find check exception useful? Even for exceptions related to business such as mmm an duplicate login? Seeing this kind of exceptions in the signature warn us that we have to deal with it in a special way, for example adding an error message to the action, to warn the user. If we don’t have these kind of warnings, we may forget to deal with it when testing the action, although the login service was dealing with this case. I have trouble refraining myself from using checked exceptions for these cases for now. But I would love to know why I’m wrong
@Florence,
Perhaps you could convince me on business level exceptions, but why exactly do we need them? To remind you that you need to catch business level error? I think you should have lots of tests to prove that the exceptions are covered, since declaring it is no guarantee that you are handling it correct, but test is a pretty good example that you are handling it correct. And if you have test, what do you need an exception declaration for.
You are right, I would be a lot less negative about checked exceptions if they were being used more sparingly, but they are not. Also, I find it interesting that there not many other languages which use them (I don’t know of any). Would you claim that Java has less bugs because of them? I think not, so why make me type all these things.
Perhaps what we need are hints. Yes, tell me which exceptions you will throw, but don’t make me catch them if I know i don’t need to.
Hey happy New Year !
“why exactly do we need them? To remind you that you need to catch business level error”
)
=> “need” might be a strong word, it is more about knowing which (business) exception a certain method can throw, so that I can deal with it if I want, like a warning. Sure I can have a look on the tests to see which (business) exception a service can throw but it is even clearer if it is on the signature. And what if we don’t have the sources? Worth what if there are no tests (yeaaaah then maybe I should not use the API at all
You have a point with the other languages. I wonder how it is done to know which (business) exception can be thrown by a service. Do we just forget to deal with it? Do we deal with all the exception in only one way? Is there more test in the others languages?
I guess you said it all with : “Yes, tell me which exceptions you will throw, but don’t make me catch them if I know i don’t need to.”
How to handle checked and unchecked exception.
Use checked and unchecked but make checked exceptions show compiler WARNINGS rather than ERRORS. Then use Suppress annotations to clean those up if you like.
I personally would not make an exception checked vs not checked based on which class it extended from but rather based on which ones were declared via the throws syntax and which ones were not.
Leave a Comment