19

In Java it is observed that there is a convention of re-throwing a RuntimeException just after handling a Checked Exception.

This way has both good and bad consequences. When the compiler forces something to be handled via a Checked Exception, the developer can just get rid of it by catching it and re-throwing it as a RuntimeException.

Can someone explain if this scenario can be considered as a good practice? If so, would this approach be less error prone or would it make the code base unstable?

Philipi Willemann
  • 658
  • 1
  • 9
  • 25
MCF
  • 768
  • 2
  • 6
  • 21
  • 4
    C# doesn't support checked exceptions at all, so it is definitely possible to create stable codebases using only runtime-exceptions. It's probably more about developer discipline than anything else. – Henrik Aasted Sørensen May 28 '13 at 14:16

5 Answers5

30

Actually it is the incompetent attempts at handling checked exceptions which result in an unstable code base. Typically, you'll have this:

try {
   //stuff
} catch (IOException e) {
   log.error("Failed to do stuff", e);
   throw e;
}

and then next level up you'll have to deal with it again, typically logging it all over and making a mess of the log files. It will be even worse if you don't rethrow:

try {
   // do stuff
} catch (IOException e) {
  return null;
}

Now the calling code has no idea something went wrong, let alone what. Compared to those attempts, this actually accomplishes exactly what the application logic needs:

try {
  // do stuff
} catch (IOException e) {
  throw new RuntimeException(e);
}

Now the exception can freely propagate up the call stack until it reaches the well-defined exception barrier, where it:

  1. aborts the current unit of work;
  2. gets logged at a single, unified spot.

In a nutshell, to decide whether to catch-and-handle or catch-and-rethrow, just ask yourself this question:

Must the occurrence of this exception abort the current unit of work?

  • if yes: rethrow an unchecked exception;
  • if no: provide meaningful recovery code in the catch-block. (No, logging is not recovery).

From many years of real-life experience I can tell you that more than 90% of all possible checked exceptions are of the "aborting" type and need no handling at the place of occurrence.

Argument against the language feature of checked exceptions

Today, checked exceptions are widely recognized as a failed experiment in language design, and here's the key argument in a nutshell:

It is not up to the API creator to decide on the semantics of its exceptions in the client code.

Java's reasoning is that exceptions can be divided into

  1. exceptions resulting from programming errors (unchecked);
  2. exceptions due to circumstances outside of programmer's control (checked).

While this division may be real to some extent, it can be defined only from the perspective of client code. More to the point, it is not a very relevant division in practice: what truly matters is at what point the exception must be handled. If it is to be handled late, at the exception barrier, nothing is gained by the exception being checked. If handled early, then only sometimes there is a mild gain from checked exceptions.

Practice has confirmed that any gains afforded by checked exceptions are dwarfed by real-life damage done to real-life projects, as witnessed by every Java professional. Eclipse and other IDEs are to blame as well, suggesting inexperienced developers to wrap code in try-catch and then wonder what to write in the catch-block.

Whenever you encounter a method which throws Exception, you have found yet another living proof of the deficiency of checked exceptions.

Community
  • 1
  • 1
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • for the first two examples in my experience in most cases it is better not to catch than to do the things you described – Marco Forberg May 28 '13 at 14:38
  • 1
    In my opinion this is terribly wrong and should not have been accepted. If you don't want to log and re-throw a checked exception then simply declare the exception (`throws CheckedException`) and otherwise do nothing about it, let the caller handle it or declare it in the same manner. – devconsole May 28 '13 at 14:46
  • The problem here is logging the exception in all the levels. Not doing so would solve the problem and there would be no need to throw a RuntimeException. – Pablo May 28 '13 at 14:48
  • I agree with this entirely - I suspect the other commenters have not actually read the article properly (i.e the first two examples are real-life examples, the third is a best-practice). – Dave Richardson May 28 '13 at 15:02
  • @DaveRlz He says that the reason to throw a unchecked exception is to abort the current unit of work and to log them all at a single spot. That can also be accomplished with checked exceptions, so it's not a very good reason to do it that way. – Pablo May 28 '13 at 15:25
  • 7
    @devconsole If your advice is followed, it will end up in all interface methods declaring `throws Exception`. There is no telling what the underlying implementation may throw. At the semantic level, `throws Exception` has exactly the same content as no `throws` clause at all. – Marko Topolnik May 28 '13 at 15:31
  • 3
    @Pablo - To have checked exceptions propagated up the call stack also means that you'll need to declare all of your functions with a 'throws', this soon clutters your code. A cleaner way is to convert it to an unchecked exception and catch it where you are interested. Take a look at Bob Martins book (Clean Code - http://www.amazon.co.uk/dp/0132350882/?tag=hydra0b-21&hvadid=9550951749&ref=asc_df_0132350882) on the section where it discusses this very point. – Dave Richardson May 28 '13 at 15:31
  • @DaveRlz I have not said that you have to always throw a checked exception. Sometimes it is appropiate to throw a checked exception, and sometimes it is apropiate to throw an unchecked one. I explained this in my answer. But this answer is advocating only two options: to handle it, or to throw an unchecked exception. – Pablo May 28 '13 at 15:37
  • 1
    @Pablo My point is that checked exceptions *promote coding errors*, as witnessed by Java professionals worldwide. I definitely do not claim that they force them; however it is quite ironic that a feature specifically introduced to protect inexperienced coders from errors has had the exact opposite effect. – Marko Topolnik May 28 '13 at 17:15
  • @Marko Topolnik - I think an interface should define which exception may be thrown by the implementations, it's part of the interface's contract. It may even come with a new exception class specifically for that purpose. For example interface java.sql.Statement throws SQLException and any other exception can be converted to SQLException if necessary (chaining). The point is the client knows exactly what exception it has to expect (if done correctly which admittedly is often not the case). – devconsole May 29 '13 at 13:05
  • 1
    @devconsole The client *cannot possibly know* which exceptions it has to expect because it can always get any `RuntimeException` or `Error`. There is no way around this fact. – Marko Topolnik May 29 '13 at 13:06
  • After all, when programming against interfaces it has to be possible to replace one implementation with another. Then it won't do when the new implementation throws different and totally unexpected exceptions than the original one. – devconsole May 29 '13 at 13:09
  • Yes, that is why unchecked exceptions are reserved for situations where the client cannot do anything to recover from the error, see Beryllium's answer. I think that applies primarily to programming errors. (ClassCastException, IllegalArgumentException, ... all fall into that category.) – devconsole May 29 '13 at 13:12
  • @devconsole When a piece of code gets an "unexpected exception", it behaves exactly as it should---the exception terminates that method and travels up the call stack until it arrives to the place where it is expected. That's the beauty of the whole system, and checked exceptions only impede it. – Marko Topolnik May 29 '13 at 13:12
  • @devconsole Whether `ClassCastExcption`, or any other, is a programmer error cannot possibly be decided by the code. Or do you perhaps routinely write something along the lines of `catch (RuntimeException e) { notifyTheGuiltyProgrammer(); } catch (Exception e) { signalAnAlarm(); }` – Marko Topolnik May 29 '13 at 13:15
  • 1
    @Marko Topolnik - How can a `ClassCastException` ever not indicate a programming error? And no, I don't do that, I let unchecked exceptions fall through up to the top-level exception barrier (where I would indeed do something like notifyTheGuiltyProgrammer()), but I do handle and if necessary convert checked exceptions at intermediate levels where they can be handled differently and more properly. – devconsole May 29 '13 at 13:25
  • @devconsole It's quite simple, just use your imagination. Let me give you an example off the top of my head: there is a column in the database which contains class names. The invariant is that those names resolve to existing classes. Someone comes along and writes a rogue value in there; we get a `ClassCastException`. The solution will not involve changing any line of code. – Marko Topolnik May 29 '13 at 13:28
  • @devconsole It is either *clearly specified* how to handle a given exception at a given point, and then this is a part of business logic; or the exception amounts to a failure. For the former case we *may sometimes* successfully employ checked exceptions. In the latter case, if the exception received is checked, it will just make us write boilerplate code to wrap into an unchecked exception and rethrow. – Marko Topolnik May 29 '13 at 13:31
  • @Marko Topolnik - Your programmer should have called `Class.isAssignableFrom(Class)` when using class names from external untrusted sources. So in my opinion it still is a programming error. Your last comment on business logic and checked exceptions might be getting us somewhere but I think we might as well agree to differ. – devconsole May 29 '13 at 13:39
  • @devconsole Ah, there we go... and what should my programmer write when it returns false? Would it be... `throw new XxxException();`? Would that exception be checked? If so, why? – Marko Topolnik May 29 '13 at 13:41
  • @Marko Topolnik - Sorry for the _your_ programmer. I see that you are right, there can be situations where a runtime exception does not indicate a programmer's error. But I still believe unchecked exceptions should be used only if the client cannot do anything to recover from the exception. Checked exceptions when the client can be expected to recover from the situation. And when you think about it that is not very far from what you said about business logic and checked exceptions. – devconsole May 29 '13 at 13:53
  • @devconsole The essential difference is that the decision whether something can be done about an exception must be left to the *client*, therefore no public library API has a case to throw checked exceptions. They have limited utility only inside client code, where the same team controls both sides (throwing and catching). That's the point I'm trying to drive in my answer. – Marko Topolnik May 29 '13 at 13:56
3

The idea of checked exceptions is "Java only" - as far as I know, no language after Java adopted this idea.

There are too many checked exceptions which are caught ... and silently ignored.

If you look at Scala, they dropped it as well - it's only there for Java compatibility.

In this tutorial on Oracle's web site, you will find this definition:

If a client can reasonably be expected to recover from an exception, make it a checked exception.
If a client cannot do anything to recover from the exception, make it an unchecked exception.

This notion has been adopted in Scala as well, and it works fine.

Technically speaking your proposal works. Discipline and code reviews are required in either way.

Beryllium
  • 12,808
  • 10
  • 56
  • 86
  • The idea that you can know when a client "can reasonably be expected to recover from an exception" has been confirmed as an illusion. Why wouldn't the client be able to recover from an NPE? Personally, I never got that idea at all. – Marko Topolnik May 28 '13 at 16:15
  • No, I do never recover from NPEs. If a NPE is something you *expect*, you can always check this before - no need to have an expensive exception with stacktrace, just a comparison before. If it's *unexpeceted* (programming error, API changed), how can you fix this? – Beryllium May 28 '13 at 16:30
  • And what if it happens three API levels down? Have you really never encountered a situation where you receive an NPE resulting from a private property of an object you don't even know about being null? At least you must admit that it is an *imaginable* scenario. For example, you may need to work around a deficiency in code outside of your control. – Marko Topolnik May 28 '13 at 16:32
  • No, that never happens :-) I do always know whether a property can be null by using *Options* (like Scala's Option). Any other properties must never be null. Options enforce to check for content before, so the compiler just refuses to compile the source. Third party libraries get an adapter or they are fixed using aspectj. – Beryllium May 28 '13 at 16:46
  • OK, so *you personally* will go as far as introducing another dependency and significantly raising the total complexity of the project even if all you need is a single NPE workaround. With that kind of attitude you are not representative of the wider Java population; but even if you were, you still cannot deny that such a thing is *imaginable*. The inventors of checked exceptions want us to believe it is unimaginable. – Marko Topolnik May 28 '13 at 17:06
  • We could discuss this for good. We agree to disagree :-) – Beryllium May 28 '13 at 17:10
1

The term "can just get rid of it" is not totally correct in this case. This is getting rid of exceptions:

 try {

 } catch (Exception e){
     e.printStacktrace();
 } 

This is the most common bad practice among the try-catch use. You are catching the exception and then, just printing it. In this case, the catch block catches the exception and just prints it, while the program continues after the catch block, as if nothing had happened.

When you decide to catch a block instead of throwing an exception, you must be able to manage the exception. Sometimes exceptions are not manageable and they must be thrown.

This is something you should remember:

If the client can take some alternate action to recover from the exception, make it a checked exception. If the client cannot do anything useful, then make the exception unchecked. By useful, I mean taking steps to recover from the exception and not just logging the exception.

If you are not going to do something useful, then don't catch the exception. Re-throwing it as a RuntimeException has a reason: as stated before, the program just cannot continue as nothing happened. This way, a good practice would be:

try {

} catch (Exception e){
    //try to do something useful  
    throw new RuntimeException(e); 
}

This means: you just caught an Exception (like an SQLException) from which you can't recover without stopping and resetting the thread. You catch it, you try to make something in between (like resetting something, closing open sockets, etc...) and then you throw a RuntimeException().

The RuntimeException will suspend the whole thread, avoiding the program continue as if nothing have happened. Furthermore, you were able to manage the other exception without just printing it.

António Ribeiro
  • 4,129
  • 5
  • 32
  • 49
aran
  • 10,978
  • 5
  • 39
  • 69
0

It may or may not be okay, depending on the context, but it probably is not.

As a rule of thumb RuntimeExceptions should only be used to indicate programming errors (examples include IllegalArgumentException and IllegalStateException). They don't have to be checked exceptions because you generally assume your program is correct until proven otherwise and you cannot handle these exceptions in a meaningful manner (you have to release an updated version of the program).

Another valid use of runtime exceptions is when you use a framework that will catch and handle the exception for you. In such a scenario it would only be burdensome to having to declare the exception in every method when you are not going to handle it anyway.

So generally speaking I would say re-throwing a checked exception as a runtime exception is very bad practice unless you have a framework that will handle it properly.

devconsole
  • 7,875
  • 1
  • 34
  • 42
  • I'm sorry, but what relevance it has to the code whether an exception is "a programming error" or just a "fatal failure"? And how could that even be decided by the very program *that contains the error*? All the developer needs is a stacktrace in the logs, so he can find out the root cause of the exception. A `FileNotFoundException` may as much be a programming error as an unforturnate runtime circumstance. – Marko Topolnik May 28 '13 at 15:54
  • Second, each well-written program must have an *exception barrier*. Whether it is in the framework, or in your own code is of no relevance. It is the barrier that handles all aborting exceptions uniformly: log them, clean up after the failed request, and move on. – Marko Topolnik May 28 '13 at 15:55
0

The general rule is: you throw a checked exception when the caller might be able to do some kind of recovery when informed about it. Otherwise, throw an unchecked exception.

This rule applies when an exception is first thrown.

But this also applies when you catch an exception and are wondering whether to throw a checked or unchecked exception. So there is no convention to throw a RunTimeException after catching a checked one. It is decided in a case-by-case basis.

One small tip: if you are going to just re-throw an checked exception after catching one and do nothing else, most of the time it is alright to just not catch the exception and add it to the exceptions thrown by the method.

Pablo
  • 3,655
  • 2
  • 30
  • 44