In Java, when should I create a checked exception, and when should it be a runtime exception? [duplicate]
Asked Answered
H

14

99

Possible Duplicate:
When to choose checked and unchecked exceptions

When should I create a checked exception, and when should I make a runtime exception?

For example, suppose I created the following class:

public class Account {
    private float balance;

    /* ... constructor, getter, and other fields and methods */

    public void transferTo(Account other, float amount) {
        if (amount > balance)
            throw new NotEnoughBalanceException();
        /* ... */
    }
}

How should I create my NotEnoughBalanceException? Should it extend Exception or RuntimeException? Or should I just use IllegalArgumentException instead?

Hydrotaxis answered 31/1, 2009 at 19:57 Comment(6)
Really looking forward to the answers here. I've become sufficiently disenfranchised with checked exceptions that I don't think answer from me would actually be useful/helpful, but I'll be fascinated to see answers from others :)Awoke
Hi Jon. Sorry, what does "disenfranchised" mean?? :)Hydrotaxis
It means Jon is taking a break to let the rest of us catch up. =)Cofferdam
Thanks @cletus, it didn't appear in my search. But I'll leave my question open because I'm getting more answers from different perspectives.Hydrotaxis
@Hosam: I actually meant disenchanted. Disenfranchised is when you lose the right to vote (or when it's denied to you illegally).Awoke
Is this really an illegal argument or an exception of any kind? You're getting some good answers on both sides of the issue, but your specific example doesn't look like a candidate for any Exception.Typesetter
W
108

There's a LOT of disagreement on this topic. At my last job, we ran into some real issues with Runtime exceptions being forgotten until they showed up in production (on agedwards.com), so we resolved to use checked exceptions exclusively.

At my current job, I find that there are many who are for Runtime exceptions in many or all cases.

Here's what I think: Using CheckedExceptions, I am forced at compile time to at least acknowledge the exception in the caller. With Runtime exceptions, I am not forced to by the compiler, but can write a unit test that makes me deal with it. Since I still believe that the earlier a bug is caught the cheaper it is to fix it, I prefer CheckedExceptions for this reason.

From a philosophical point of view, a method call is a contract to some degree between the caller and the called. Since the compiler enforces the types of parameters that are passed in, it seems symmetrical to let it enforce the types on the way out. That is, return values or exceptions.

My experience tells me that I get higher quality, that is, code that JUST WORKS, when I'm using checked exceptions. Checked exceptions may clutter code, but there are techniques to deal with this. I like to translate exceptions when passing a layer boundary. For example, if I'm passing up from my persistence layer, I would like to convert an SQL exception to a persistence exception, since the next layer up shouldn't care that I'm persisting to a SQL database, but will want to know if something could not be persisted. Another technique I use is to create a simple hierarchy of exceptions. This lets me write cleaner code one layer up, since I can catch the superclass, and only deal with the individual subclasses when it really matters.

Waits answered 31/1, 2009 at 20:11 Comment(3)
Don Branson wrote "My experience tells me that I get higher quality, that is, code that JUST WORKS, when I'm using checked exceptions." - not so sure that by simply using checked exceptions and ending up with your code pollution with tons of empty/re-throw catch blocks...might result in higher quality code. I'd say more unit tests would result in higher quality code - not exception type chosen!Ruffian
"ending up with your code pollution with tons of empty/re-throw catch blocks" - if you see this, they're not being used correctly. You can take any technique, use it wrong, and offer up a critique of it.Waits
As far as unit tests are concerned, i agree that the code should be unit-tested. TDD'd, actually. But remember the expense continuum. That same continuum that makes bugs detection cheaper in a unit test than in production, makes bugs detected by the compiler cheaper than those caught by a UT.Waits
P
46

In general, I think the advice by Joshua Bloch in Effective Java best summarises the answer to your question: Use checked expections for recoverable conditions and runtime exceptions for programming errors (Item 58 in 2nd edition).

So in this case, if you really want to use exceptions, it should be a checked one. (Unless the documentation of transferTo() made it very clear that the method must not be called without checking for sufficient balance first by using some other Account method - but this would seem a bit awkward.)

But also note Items 59: Avoid unnecessary use of checked exceptions and 57: Use exceptions only for exceptional conditions. As others have pointed out, this case may not warrant an exception at all. Consider returning false (or perhaps a status object with details about what happened) if there is not enough credit.

Petronille answered 31/1, 2009 at 20:48 Comment(7)
I think that in Java there should be no methods returning a boolean to indicate success or failure. The chance is just too high that the caller may forget to check the returned value. Throwing an exception in this case seems more appropriate, and this exception may then carry your suggested status object.Til
@Roland: Fair point; no strong opinion about that so I'm not gonna argue. :-) What I'm mainly saying is that if some "NotEnoughBalanceException" is thrown here, it should be certainly be checked, as per the rule of thumb I quoted.Petronille
@Roland: I'd disagree with your blanket assertion but not with your principle. :-) It's no different from Jonik's Bloch quote: use exceptions for exceptional conditions. If the method might reasonable "fail" in normal operation (e.g. Collection.add) then a success-valued return is sensible. If failure is a result of unexpected coditions (e.g. saving to file) than an exception is appropriate. Whether bad developers can shoot themselves in the foot is a marginal concern, because they'll find a way regardless.Polysepalous
Not forcing the dev to check a return value on most calls is why Exceptions were introduced in the first place...Crochet
@Crochet But still, exceptions should be exceptional–they shouldn't occur on most calls as a matter of simple flow control. So while exceptions are valuable, they do not and should not take the place of return values that indicate the success or failure of something, if failure doesn't mean an exception should be thrown. If failure can be successfully handled or mitigated such that program execution can continue, and it is a normal, expected occurrence, then that failure isn't exceptional, and doesn't merit an exception being thrown, and it should be a return value.Sweatbox
@Sweatbox A fact of which I'm well-aware: https://mcmap.net/q/56402/-are-nested-try-catch-blocks-a-bad-ideaCrochet
@Crochet Okay. It seems to me that my comment balances things a bit in this answers comments, regardless of what any person's personal philosophies or knowledge are.Sweatbox
W
35

When to use checked exceptions? Honestly? In my humble opinion... never. I think it's been about 6 years since I last created a checked exception.

You can't force someone to deal with an error. Arguably it makes code worse not better. I can't tell you the number of times I've come across code like this:

try {
  ...
} catch (IOException e) {
  // do nothing
}

Whereas I have countless times written code like this:

try {
  ...
} catch (IOException e) {
  throw new RuntimeExceptione(e);
}

Why? Because a condition (not necessarily IOException; that's just an example) wasn't recoverable but was forced down my throat anyway and I am often forced to make the choice between doing the above and polluting my API just to propagate a checked exception all the way to the top where it's (rightlfully) fatal and will be logged.

There's a reason Spring's DAO helper classes translate the checked SQLException into the unchecked DataAccessException.

If you have things like lack of write permissions to a disk, lack of disk space or other fatal conditions you want to be making as much noise as possible and the way to do this is with... unchecked exceptions (or even Errors).

Additionally, checked exceptions break encapsulation.

This idea that checked exceptions should be used for "recoverable" errors is really pie-in-the-sky wishful thinking.

Checked exceptions in Java were an experiment... a failed experiment. We should just cut our losses, admit we made a mistake and move on. IMHO .Net got it right by only having unchecked exceptions. Then again it had the second-adopter advantage of learning from Java's mistakes.

Wast answered 31/1, 2009 at 22:16 Comment(11)
I agree with this; I have used runtime exceptions exclusively over an extensive codebase (about 2000 classes evenly split between library and application) and have not had practical problems in production with unhandled exceptions.Adley
Cletus: +1 for a good answer over, but I think the linked article by ckwop is complete crap - my advice would be to remove it. Seriously. Simon (ckwop) recommends we go back to errors indicated by return codes, for heavens sake!Adley
Unchecked exceptions make your abstractions leak... if you have them. If your design is relatively unstable, and calls for more concrete code, unchecked exceptions fit alright. In a stable, abstract layer of your system, checked exceptions preserve the abstraction.Aleksandr
Unchecked exceptions are no more or less leaky than checked exceptions. Declaring your code throws an IOException or a SQLException says something about your implementation so you can have a special checked exception but you can have a special unchecked exception too. It's no different.Wast
If a service layer declares a UserAlreadyExistsException, the client can catch that and recover. If the data access layer is simply letting unchecked Hibernate ConstraintViolationExceptions bubble up, any caller that wants to recover from that specific problem has a compile-time dependency on a particular implementation. Checked exceptions encourage implementations of abstractions to re-write low level exceptions into a form appropriate for the abstraction, preventing implementation leakage.Aleksandr
UserAlreadyExistsException can be an unchecked exception too. The client can still catch it. The only difference is they aren't forced to deal with it when they might have no interest in catching it.Wast
cletus - What's interesting about that "exceptions break encapsulation" link is that the majority of his objections (including the eponymous one) are invalidated if you use checked exceptions. Exception throwing isn't invisible if they're checked. Exceptions don't break encapsulation if the method declares it throws its exception as part of its contract. And if a developer can't be trusted to handle checked exceptions correctly, with the compiler's help, why on earth would they be expected to handle unchecked exceptions correctly?Polysepalous
Fundamentally the only difference between checked exceptions and unchecked exceptions is that the compiler forces you to document checked exceptions. You should be documenting any exceptions your code can throw anyway; I can't see why anyone would be against this being statically checkable to prevent it from being stale or from any exceptions being missed out. Exceptions are part of your API whether checked or not; checked just ensures you declare your API accurately.Polysepalous
Andrzej Doyle wrote "Exceptions are part of your API whether checked or not; checked just ensures you declare your API accurately." – not so...if checked exception declared compiler also forces to catch that exception regardless if you know how to handle it or not! That is the big difference! It will pollute your code with tons of empty/re-throw catch blocks...Ruffian
@user1697575, Like every other trade, there is a trade-off. The advantage of having the compiler do the checks means that there is no way to accidentally leak exceptions (and humans do make mistakes). If the calling code does not handle the exception, it has to leak it explicitly. This is exactly the same trade-off we get from loosely-typed vs strongly-typed languages.Austerlitz
@AndrzejDoyle Checked exceptions make it impossible to get 100% code coverage for your tests, force pointless try/catch blocks when you want exceptions to bubble up and generally add noise/clutter for little value. Now if you could declare exceptions in the signature but not force them to be handled, that would be ideal as it would document but not pollute codeCrochet
F
8

IMHO, it shouldn't be an exception at all. An exception, in my mind, should be used when exceptional things happen, and not as flow controls.

In your case, it isn't at all an exceptional status that someone tries to transfer more money than the balance allows. I figure these things happen very often in the real world. So you should program against these situations. An exception might be that your if-statement evaluates the balance good, but when the money is actually being subtracted from the account, the balance isn't good anymore, for some strange reason.

An exception might be that, just before calling transferTo(), you checked that the line was open to the bank. But inside the transferTo(), the code notices that the line isn't open any more, although, by all logic, it should be. THAT is an exception. If the line can't be opened, that's not an exception, that's a plausible situation.

IMHO recap: Exceptions == weird black magic.

being-constructive-edit:

So, not to be all too contradictive, the method itself might very well throw an exception. But the use of the method should be controlled: You first check the balance (outside of the transferTo() method), and if the balance is good, only then call transferTo(). If transferTo() notices that the balance, for some odd reason, isn't good anymore, you throw the exception, which you diligently catch.

In that case, you have all your ducks in a row, and know that there's nothing more you can do (because what was true became false, as if by itself), other than log the exception, send a notification to someone, and tell the customer politely that someone didn't sacrifice their virgins properly during the last full moon, and the problem will be fixed at the first possible moment.

less-enterprisey-suggestion-edit:

If you are doing this for your own pleasure (and the case seems to be this, see comments), I'd suggest returning a boolean instead. The usage would be something like this:

// ...
boolean success = transferTo(otherAccount, ONE_MILLION_DOLLARS_EXCLAMATION);

if (!success) {
  UI.showMessage("Aww shucks. You're not that rich");
  return; // or something...
} else {
  profit();
}
// ...
Fitts answered 31/1, 2009 at 20:1 Comment(6)
Thanks, but you took this much more seriously than I intended. Very informative nonetheless, but it doesn't really address my question. This is a very simple class that I'm doing for a personal budget tracking application, and it's more of a learning project than a full-fledged application.Hydrotaxis
But what do you suggest to use other than the exception? Assuming the account doesn't allow negative balance, what should I do when the parameter would cause this situation?Hydrotaxis
Other than exception, you have only error codes. But exception gives you full object you can construct and return.Hymenopteran
Thanks a lot @wolfie. I originally had my method return an instance of class Transaction (although I didn't include it in my sample). Would you recommend checking that the return is not null? I feel that, from the Account perspective, it's a fairly illegal argument. I appreciate your help.Hydrotaxis
@Hosam: If the method returns an object, yes, I'd use a returned null as an indicator of an error-status, instead of throwing an exception.Fitts
Checked exceptions are the best way to be notified of errors due to race conditions, such as another transaction having captured the funds between the time you checked for them and when you actually tried to transfer them. There's really no point in doing a "pre-check" in a concurrent system.Aleksandr
W
8

My rule is

  • if statements for business logic errors (like your code)
  • cheched exceptions for environment errors where the application can recover
  • uncheched exception for environment errors where there is no recovery

    1. Example for checked exception: Network is down for an application that can work offline
    2. Example for uncheched exception: Database is down on a CRUD web application.

There is much documentation on the subject. You can find a lot by browsing the Hibernate web pages since they changed all exceptions of Hibernate 2.x from checked to unchecked in version 3.x

Watthour answered 31/1, 2009 at 20:42 Comment(4)
Recoverability is in the "eye" of the caller; i.e., the implementor has no business presuming what the client can or can't recover from. The convention is that programming errors result in runtime exceptions, while the rest are checked.Aleksandr
The convention according to whom?Watthour
A convention is a generally followed practice without an authoritative mandate, a de facto standard rather than a standard du jour. But if you are looking for authorities that hold this view, James Gosling and Josh Bloch are among them.Aleksandr
I think you are confusing two things. Checked/unchecked exceptions in libraries (where Recoverability is indeed in the eye of the caller) and checked/unchecked exceptions in my internal code where I am both the producer and the consumer.Watthour
Z
8

I recently had a problem with exceptions, code threw NullPointerException and I had no idea why, after some investigation it turned out that real exception was swallowed(it was in new code, so its still being done) and method just returned null. If you do checked exceptions you must understand that bad programmers will just try catch it and ignore exception.

Zymosis answered 1/2, 2009 at 17:28 Comment(0)
C
5

My feeling is that the checked exception is a useful contract that should be used sparingly. The classic example of where I think a checked exception is a good idea is an InterruptedException. My feeling is that I do want to be able to stop a thread / process when I want it to stop, regardless of how long someone has specified to Thread.sleep().

So, trying to answer your specific question, is this something that you absolutely want to make sure that everyone deals with? To my mind, a situation where an Account doesn't have enough money is a serious enough problem that you have to deal with it.

In response to Peter's comment: here's an example using InterruptedException as concrete case of an exception that should be handled and you need to have a useful default handler. Here is what I strongly recommend, certainly at my real job. You should at least do this:

catch (InterruptedException ie) {
    Thread.currentThread().interrupt();
}

That handler will ensure that the code catches the checked exception and does exactly what you want: get this thread to stop. Admittedly, if there's another exception handler / eater upstream, it's not impossible that it will handle the exception less well. Even so, FindBugs can help you find those.

Now, reality sets in: you can't necessarily force everyone who writes an exception handler for your checked exception to handle it well. That said, at least you'll be able to "Find Usages" and know where it is used and give some advice.

Short form: you're inflicting a load the users of your method if you use a checked exception. Make sure that there's a good reason for it, recommend a correct handling method and document all this extensively.

Chartier answered 31/1, 2009 at 20:13 Comment(3)
Unfortunately so many people don't understand InterruptedException :-( I too believe it should be checked, but many people just ignore it by empty catch block.Hymenopteran
Personally, I think InterruptedException is a great example of where an unchecked should have been used.Adley
How could that possibly be better? If you are in the middle of something like a get() without a timeout and you receive an InterruptedException, you are expected to interrupt the thread. A checked exception is a pointed reminder that this is an exceptional and important case that you MUST handle.Chartier
A
5

From Unchecked Exceptions -- The Controversy:

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.

Note that an unchecked exception is one derived from RuntimeException and a checked exception is one derived from Exception.

Why throw a RuntimeException if a client cannot do anything to recover from the exception? The article explains:

Runtime exceptions represent problems that are the result of a programming problem, and as such, the API client code cannot reasonably be expected to recover from them or to handle them in any way. Such problems include arithmetic exceptions, such as dividing by zero; pointer exceptions, such as trying to access an object through a null reference; and indexing exceptions, such as attempting to access an array element through an index that is too large or too small.

Aludel answered 20/11, 2010 at 17:10 Comment(2)
Unfortunately, there are many situations in which some clients will be expected to recover from exceptions, but many won't. Further, there are many situations in which the possibility of a method throwing an exception will depend upon the parameters passed to it. Use of a checked exception for something which in many usage cases can never be thrown forces callers to add code which can only be executed in cases where something is so severely wrong as to render meaningful recovery impossible.Massachusetts
try {URL u = new URL(“http://example.com/”);} catch (MalformedURLException e) { /*Can _never_ execute as the url is static but we have to handle it anyway*/ }. This prevents code coverage of 100% on tests and adds utterly pointless clutter. There is no way URL can know how it will be used so the implementation makes assumptions that are bound to be wrong some of the time. How is that ever a good idea?Crochet
S
4

A checked exception means that clients of your class are forced to deal with it by the compiler. Their code cannot compile unless they add a try/catch block.

The designers of C# have decided that unchecked exceptions are preferred.

Or you can follow the C-style and check return values and not throw exceptions.

Exceptions do have a cost, so they shouldn't be used for control flow, as noted earlier. But the one thing they have going for them is that they can't be ignored.

If you decide that in this case to eschew exceptions, you run the risk that a client of your class will ignore the return value or fail to check the balance before trying to transfer.

I'd recommend an unchecked exception, and I'd give it a descriptive name like InsufficientFundsException to make it quite clear what was going on.

Sole answered 31/1, 2009 at 20:9 Comment(2)
You don't have to add a try-catch. If a caller isn't able to handle the exception, it should simply declare it and do nothing.Aleksandr
Somewhere up the line someone has to handle it. Saying that you can't, and passing responsibility up the line, is making a decision too. You do force them to acknowledge the need for handling and choose.Sole
S
3

Line is not always clear, but for me usually RuntimeException = programming errors, checked exceptions = external errors. This is very rough categorization though. Like others say, checked exceptions force you to handle, or at least think for a very tiny fraction of time, about it.

Svoboda answered 31/1, 2009 at 20:11 Comment(0)
V
3

Simply put, use checked exception only as part of external contract for a library, and only if the client wants/needs to catch it. Remember, when using checked exception you are forcing yourself on the caller. With runtime exception, if they are well-documented, you are giving the caller a choice.

It is a known problem that checked exceptions are over-used in Java, but it doesn't mean that they are all bad. That's why it is such in integral part of the Spring philosophy, for example (http://www.springsource.org/about)

Valletta answered 31/1, 2009 at 20:21 Comment(0)
E
3

The advantage of checked exceptions is that the compiler forces the developer to deal with them earlier. The disadvantage, in my mind anyway, is that developers tend to be lazy and impatient, and stub out the exception-handling code with the intention of coming back and fixing it later. Which, of course, rarely happens.

Bruce Eckel, author of Thinking in Java, has a nice essay on this topic.

Ellieellinger answered 31/1, 2009 at 20:26 Comment(1)
try {URL u = new URL(“http://example.com/”);} catch (MalformedURLException e) { /*Utterly pointless catch block we're forced to create*/ }Crochet
C
3

I don't think the scenario (insufficient funds) warrants throwing an Exception --- it's simply not exceptional enough, and should be handled by the normal control flow of the program. However, if I really had to throw an exception, I would choose a checked exception, by extending Exception, not RuntimeException which is unchecked. This forces me to handle the exception explicitly (I need to declare it to be thrown, or catch it somewhere).

IllegalArgumentException is a subclass of RuntimeException, which makes it an unchecked exception. I would only consider throwing this if the caller has some convenient way of determining whether or not the method arguments are legal. In your particular code, it's not clear if the caller has access to balance, or whether the whole "check balance and transfer funds" is an atomic operation (if it isn't then the caller really has no convenient way of validating the arguments).

EDIT: Clarified my position on throwing IllegalArgumentException.

Cofferdam answered 31/1, 2009 at 20:28 Comment(5)
Thanks @Zach. I've edited my question to clarify that the class provides a getter to retrieve the value. So your recommendation is to throw an IllegalArgumentException? Or would you recommend creating my own unchecked exception?Hydrotaxis
How would you handle a race condition?Aleksandr
@erickson: I don't understand the question. Could you elaborate please?Cofferdam
@Hosam: I don't recommend throwing an IllegalArgumentException because it's a runtime unchecked exception. For this scenario, a checked exception (e.g. a custom one) is more suitable because you have to handle it explicitly. Better yet, don't use exceptions at all.Cofferdam
I think you've addressed my question with the edit about atomicity. There are cases with no way for a caller to check that an action will succeed before requesting the action, due to a race. E.g., I can test whether a file exists, but another process can delete it before I can open it.Aleksandr
R
2

Myself, I prefer using checked exceptions as I can.

If you are an API Developer (back-end developer), use checked exceptions, otherwise, use Runtime exceptions.

Also note that, using Runtime exceptions in some situations is to be considered a big mistake, for example if you are to throw runtime exceptions from your session beans (see : http://m-hewedy.blogspot.com/2010/01/avoid-throwing-runtimeexception-from.html for more info about the problem from using Runtime excpetions in session beans).

Ratable answered 21/1, 2010 at 22:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.