As an Agile Coach, Miško is responsible for teaching his co-workers to maintain the highest level of automated testing culture, allowing frequent releases of applications with high quality. He is very involved in Open Source community and an author of several open source projects. Recently his interest in Test Driven Developement turned into http://TestabilityExplorer.org with which he hopes will change the testing culture of the open source community. Misko is a DZone MVB and is not an employee of DZone and has posted 38 posts at DZone. You can read more from them at their website. View Full User Profile

Checked Exceptions I Love You, But You Have to Go

09.17.2009
| 14240 views |
  • submit to reddit

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 con 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.

 

From http://misko.hevery.com

Published at DZone with permission of Misko Hevery, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Andreou Dimitris replied on Thu, 2009/09/17 - 9:28pm

I understand that some people are just too lazy to *read* the javadocs of the methods they. No wonder where all these bugs come from.

phil swenson replied on Thu, 2009/09/17 - 11:08pm

Sun has now endorsed runtime exceptions over checked exceptions.  In Java FX, all exceptions are unchecked.  So Java remains the only language ever created with checked exceptions.  I think it will remain alone.

 

 

phil swenson replied on Thu, 2009/09/17 - 11:16pm in response to: Marius Scurtescu

Checked exceptions actually tend to result in exception swallowing or catch/log/continue behavior which makes it appear that everything is fine - even though it isn't.

 Exceptions usually are a result of a system failure or a bug.  Both of these situations are usually unrecoverable, so trying to handle and proceed is the wrong approach.  You want your exception to bubble up to your top thread and be reported - via log/exception notifier/ui.

 In the event that an exception is able to be handled, then you do catch them at an appropriate place and deal with it via retries or proceeding.  But this is quite rare.

 

Loren Kratzke replied on Fri, 2009/09/18 - 2:04am

Some of the things that the author of the main article said absolutely horrify me. According to him an exception is nothing more than a nuisance that must be logged, and - oh forbid that it reduces his code coverage metric with - DEAD CODE. Where do I begin....

How about near the end of the article.

Always catch all checked exceptions at source and rethrow them as LogRuntimeException.

 WTF?

 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)

 Whoa. As I discover? You would not make it one day in an enterprise environment my friend. With that attitude you are more of a liability than an asset.

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).

What about the state of the application and data? And what does he mean by being "done with it". Does the application exit? Oh, my bosses would just love that.

    "Yes sir, ummm, the application just stops running. But it prints a message :-D"

 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.

 Perhaps 90% of your bugs are caused by bad code but it leads me to beleive that you must write really bad code. If you wrote really good code then ideally 0% of your exceptions would be caused by bad code.

To answer the question, we rethrow until we handle it.

 And when it comes to logging, you only need to log an exception event once, in the place where you handle it. The exception constructor takes a root cause (and a message) as an argument that yields a handsome stack trace when the final exception is logged.There is no need to log at every level. Why would you do that? Log it once where you handle it.

Very close to 100% test coverage as there is no dead code in my catch blocks.

 First of all, I am not a fan of test driven development. But that statement I find particularly offensive to the intellect. Just because your catch block does not have test converage does not mean that there is dead code in your catch block. Dead code is that which does not execute at runtime, not that which does not execute in your particular test case.

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.

 Ever heard of Murphy?

  • Perhaps your file isn't there because the file system was full when you thought you put the file there.
  • Perhaps the disk is corrupt.
  • Perhaps another process removed the file (multithreading and concurrency anyone?).
  • Perhaps you have a bug in your code and you only think that the file is there.

All of these would result in a FileNotFoundException. At design time, a developer can't possibly know anything at runtime as fact. Come on, you're an Agile coach.

  "As a developer I wish to receive an Exception when a file is not found so that I may redirect my application logic in a sensible way."

 I don't mean to come off as a smart ass, and I am sure that the author is a brilliant fellow in his realm, but he should really study proper exception handling techniques and spend some time developing and maintaining some mission critical apps. I think he will change his tune.

Gerd Ziegler replied on Fri, 2009/09/18 - 2:59am in response to: Jeff Knecht

@Jeff: Don't have to catch, if you cannot handle it properly just say throws Exception in your method. This makes the caller think about handling the Exception but he is not forced to do it. So if the caller sees theres no throws in your method he can be assured he has nothing to do, but if there is one he has to think about it and has to find the place where to handle the exception. No wrapper code like this is needed:

catch(AException e) {

 throw new BException(e);

}

Marius Scurtescu replied on Fri, 2009/09/18 - 3:20am in response to: phil swenson

"Checked exceptions actually tend to result in exception swallowing or catch/log/continue behavior which makes it appear that everything is fine - even though it isn't."

 Unfortunately this is true, I had to deal with way too much code like that. This is the main reason why I think that exception handling can be improved somehow. Saying that most programmers are not skilled enough is not the right answer, most likely the complexity of the language was pushed too far. Still, just dropping checked exceptions and dreaming that unchecked ones will solve the problem does not sound too realistic to me.

Gerd Ziegler replied on Fri, 2009/09/18 - 4:38am in response to: Jeff Knecht

@Jeff: Seems you did not get my intention. It was more like this:

 

if you cannot do anything meaningful here:

public void myMethod() throws Exception {

  gerdsapi.processFile(myFile);

}

or if you can do something on IOExceptions:

public void myMethod() throws Exception {

 try {
  gerdsapi.processFile(myFile);
 } catch(IOException e) {
   doSomethingReallyMeaningfulHere();
 } 
  //you do NOT have to do anything here, other exceptions fall through 

}

But you are forced to THINK if you can do something or if you want to let the exception fall through.

 

Armin Ehrenreich replied on Fri, 2009/09/18 - 4:40am in response to: phil swenson

But JavaFX is a scripting language (at least Sun says so). Scripting languages are better for small projects, as they tend to support "quick and dirty" programming. That is a different league. Java in contrast is used in large projects with many programmers because it is more formal. Therefore the requirement to handle exceptions in Java is a very bright idea in my opinion no matter whether scripting languages don't have it.

Tim O'farrell replied on Fri, 2009/09/18 - 6:15am in response to: Tony Siciliani

You cite SQLException and IOException as "accurately defined as checked exceptions", but I am afraid I have to agree with the author in this case. Each of those exceptions almost always means one of 2 things :

 - There is a bug in your code - There is generally nothing the program can do about this - gotta fix it in the next release.

 - An external resource (Such as a database) is unavailable - Again there is nothing the program can do about it - talk to the ops team.

It is a good idea to have the runtime exceptions declared on methods (informing the developer that such things can occur, but there is little point in forcing that implementation detail to be reflected in every calling method of the program stack - only where it eventually will be handled.

 

Marius Scurtescu replied on Fri, 2009/09/18 - 10:28am in response to: Tim O'farrell

"An external resource (Such as a database) is unavailable - Again there is nothing the program can do about it - talk to the ops team.", that depends on the program, there are cases when you definitely can do something. For example:

  • fallback to an alternate database
  • fallback to a default value
  • try again later (instead of killing the current thread)
In general if you are dealing with long running processes there always is an alternative, worst case you want your code to reconnect as soon as the database becomes available again.

phil swenson replied on Fri, 2009/09/18 - 10:47am in response to: Armin Ehrenreich

Ok, then why has no other language ever written used checked exceptions?

 Actually, I think you have it backwards.  Checked exceptions work ok when you have a small, simple project.  It's in large projects that they fail.  What tends to happen is the method signatures start getting unweildy with many exceptions bubbling up.  So people tend to do 1 of 2 things:  switch method signatures to "throws Exception" or start catching and (maybe) logging and proceeding as if nothing has gone wrong.

"throws exception" is harmless, but you might as well use RuntimeExceptions.  The latter is harmful.  When things go wrong, you can't proceed as if they are ok.  You likely will end up with data loss, data integrity issues, or hidden bugs.  

Loren Kratzke replied on Fri, 2009/09/18 - 4:04pm in response to: phil swenson

Checked exceptions result in exception swallowing just as stop signs result in California stops? 

"Handling" an exception does not mean that the original problem goes away. It means that you have taken steps to avoid data corruption, destablizing the application, and/or ruining the user experience.

The requirement/responsibility to take action on an exception is inversely proportional to the triviality of an application. Handling an exception is not so much about recovery as it is avoiding a major disaster if your application is mission critical.

Usually, checked exceptions have runtime exceptions as a root cause. So trading checked exceptions for runtime exceptions not only accomplishes nothing, it is a giant step backward toward writing quality software.

 A checked exception says, "Warning, I don't care how good your code is, this method could fail at runtime for reasons beyond your control. You have been warned."

To ignore that type of warning is dangerous. To make that type of warning ignorable is irresponsible.

Henk De Boer replied on Sat, 2009/09/19 - 11:55am

Just sampled several large code bases I have access to. If my sampling truly reflects the whole code base, then the numbers claimed by the author about useless catches seems to be spot on. About 97% of all catches of checked exceptions turned out to be utterly useless. They just wrap the exception in a type expected by the higher layer or indeed print it.

This is really an eye opener!

It gets worse though. I just stumbled on a few cases where the exceptions were printed, but wrongly, causing useful information to be lost. Our application has a top-level exception handler, that walks the exception chain and reports the exception by sending it as a notification to a developer monitored queue. So in our case, printing locally is actually not such a good idea, yet developers seem to have been caught (no pun intended) in the habit of catching and printing exceptions blindly.

Yet another interesting fact that my sampling revealed is that many developers on that code base seem to just catch the exception and wrap it in a RunTimeException.

One of the few approximately useful cases of exceptions being caught happened to be JPA exceptions, which were then converted to Faces messages (like, "item not found", etc). Ironic as it might be, these cases of usefully caught exceptions concerned unchecked (runtime) exceptions! (JPA throws unchecked exceptions).

Henk De Boer replied on Sat, 2009/09/19 - 12:07pm in response to: Loren Kratzke

A checked exception says, "Warning, I don't care how good your code is, this method could fail at runtime for reasons beyond your control. You have been warned." To ignore that type of warning is dangerous. To make that type of warning ignorable is irresponsible.

I'm not sure you understand fully.

You can't really ignore an unchecked exception. You can ignore error codes, but an unchecked exceptions terminates whatever your code was doing.

Now if you were doing something critical, which should not be terminated or for which some absolutely necessary clean-up code has to run, then you should guard yourself against that with a finally block anyway. Checked exceptions are NOT going to help here! This is a completely false sense of security! Unchecked exceptions (e.g. a NPE or worse an OOME) can happen anyway, so regardless of the fact whether code tells you it does or does not throw an exception you should have that guard in place.

Now you might claim that you can -prove- that your code doesn't throw a NPE, or DBZ exception etc. But in practice this requires formal verification of your code. If you have ever needed to write a formal verification for a non-trivial piece of code, then you know that nobody is ever going to do that for 'normal' business code (heck, even trivial pieces of code or still hard to formally prove correct).

Basically checked exceptions give you NOTHING, while costing you immensely. There is a very good tradeoff though between documenting behavior and easy and clean code that is still of high quality, and that's to declare unchecked (runtime) exception. Yes, in case anyone didn't know this, you can actually declare unchecked exceptions. The compiler is not going to enforce anything when you do this, but it works very good as a means of documenting your code.

Armin Ehrenreich replied on Tue, 2009/09/22 - 5:52am in response to: phil swenson

In large projects there should be a concept of the exceptions: Either they are somehow handled, so that they won't bubble up to the next level, or they are accumulated to a higher ranking application specific !! exception so that not too many occur in a certain signature.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.