really meant when he said that JavaExceptionsAreParticularlyEvil
, given the original context.
I thought that Bill was worried about slow refactoring. Do CheckedExceptions slow down refactoring?
It was the marketing person in me - I thought we'd be sure to get more hits coming off RecentChanges
if I used that name, straight from Bill's text.
s of the ManifestTyping
variety (as seen in Java) violate language design principles on many dimensions; saying 'JavaExceptionsAreParticularlyEvil
' seems quite accurate. Some points:
- Composing ManifestTyping CheckedExceptions with DesignPatterns (e.g. VisitorPattern) and FunctorObjects is painful, especially since interfaces for FunctorObjects cannot anticipate any particular class of exceptions. To maintain them in their original form requires one 'work around' the language via complicated exception-tunneling patterns. The alternatives are to catch and drop the exceptions or wrap them with an unchecked exceptions at which point you might as well have not had the ManifestTyping CheckedExceptions in the first place! (Need to workaround to fix system up after a workaround makes for some rather malodorous MissingFeatureSmells.)
- ManifestTyping CheckedExceptions is among the ToolsThatTeachPoorHabits. (discussed there).
- ResumableExceptions as seen in the CommonLispConditionSystem would offer a powerful enhancement to exception handling patterns, allowing one to separate error handling policy from the error recovery mechanism and simultaneously providing ability to abstract and compose both error-handling policies and mechanisms. However, CheckedExceptions of the ManifestTyping sort make it difficult to perform this SeparationOfConcerns. The need to annotate each method on the calling path with the exceptions would be massive friction against such separation. Between the two, ResumableExceptions are of proven value, and would have made a much better addition to Java.
- And, in general, language designers and other inventors should never try anything totally new in a language meant for use outside a laboratory. CheckedExceptions are a fine example of why this is wisdom.
s (you don't ever catch exception XYZ on path ABC), would probably resolve many of these issues. Haskell does some similar stuff with monads (Haskell does everything
with monads), and those guys seem happy with it. ImplicitTyping
would always let a programmer know when an exception is unhandled, and would allow them to make good decisions about where to catch it.
- I think that CheckedExceptionsAreOfDubiousValue: they introduce a dependency between a method and potentially all of its direct or indirect callers. The type information conveyed is not of much practical value (it seems like it ought to be, but in practice it's rarely used) for purposes of coding although it does add valuable documentation to the method. Generalizing the type of exception in the "throws" clause can go a long way toward mitigating the problem. For instance, a method that does a lot of file manipulation can put "IOException" in its "throws" clause. This will cover: EOFException, FileNotFoundException?, InterruptedIOException, and ZipException? as well as many others. Having a single base exception type for each package can also help with this problem. The dependencies are still there, but they are more general and therefore more stable. See ConvertExceptions for more information.
s have great value; however, they are frequently overused. In particular, they should never be used for programming errors, but absolutely should be used for resource errors and for flow control (see ClassifyingExceptions
for more details). In the latter cases, you really should be catching the exceptions anyway, so if you decide to change what exception is thrown, you will have to change it in both the caller and the called method. CheckedException
s allow the compiler to guide you. -- RussellGold
s can be used when a method cannot do what its name says it does. They are handy for returning out-of-band data.
...you really should be catching the exceptions anyway...
Assuming that this means: "the caller should catch all of the exceptions thrown by the callee" (or maybe all the exceptions of a particular type), can you elaborate on the reasons why this is so? I thought exceptions were invented specifically to allow the caller not
to handle exceptional circumstances handled by the callee. -- PhilGoodwin
The caller can (and should) pass the buck by adding the exception to its own type signature. But, see NestedException
for handling cross-module exceptions.
I just fixed a bug yesterday that shows the value of checked exceptions. A method returned null to indicate failure, causing a NullPointerException
in the wrong place, causing the server to fail to start in an unexpected corner case. We got complaints from customers about NullPointerException
s when the real error was that a file was missing, and it shouldn't have caused total failure of the calling method. If the called method had been correctly implemented to return a checked exception, the calling code would have been written correctly. An unchecked exception wouldn't have helped. -- BrianSlesinsky
A method returned null to indicate failure
An exception would almost certainly have been better.
the real error was that a file was missing
So the exception thrown should have indicated that.
it shouldn't have caused total failure of the calling method
Methods should throw or propagate exceptions whenever they cannot fulfill their contracts. I think that it's generally better to LetExceptionsPropagate
so I think that I would have preferred to detect the absence of the file early in order to AvoidExceptionHandlingInTheMainLine?
If the called method had been correctly implemented to return a checked exception, the calling code would have been written correctly.
That's a little bit of a leap, but I think I see what you're thinking: If the callee had fileOpenException in its throws clause, the caller would have had to either handle or propagate that exception. If you forgot to do that, the compiler would give you an error and you'd have to do something. If you declare everything ThrowsExceptionByDefault
, you lose that bit of static error checking, which is a shame, but you get much more flexibility to change the code, which I value a lot more.
I think the point you are making is a good one though. This is not a choice that should be made lightly. Static error checking is a Good Thing and shouldn't be given up lightly. You will have to do more testing to make up for its absence. The ExtremeProgramming
folks would tell you that you should be doing that anyway and, indeed, the error you describe would have been very
unlikely in an ExtremeProgramming
shop. Not all of us are doing ExtremeProgramming
however. My own personal experience is that the kind of error you describe is relatively rare and that the price for catching it at compile time (many more dependencies in the code) is too high. -- PhilGoodwin
I think the example above is merely an example that hindsight is 20/20. The writer seems to be saying that the developers did not consider the case of the file not being found, but if an explicitly named exception had been listed, then the developers would have known to handle this case. It is hard to predict what might have been done if the circumstances were different, but I would suspect it is highly likely that the exception would merely have been propagated and the same fault scenario would have occurred.
Exceptions seem to be related to *how* a task is performed, not the *purpose* of the task. In this case a file was missing. If the data was no longer stored in a file and was moved to a relational database, or an LDAP server fileOpenException would no be longer be appropriate. If the data became static the exception would go away. I want to isolate callers as much as possible from how I perform my tasks. A specific type of exception seems to go in the opposite direction. If I just throw Exception is that acceptable?
--- Wrap exceptions. For example, if you go to read preferences from a file, catch IOException and wrap it in a PreferenceReadFailedException?
. If you later change it to use a DBMS, the preference exception will wrap a database exception instead. Keep the exception in line with the abstraction level by transforming it as need be. Actually it may be best to wrap the exception by putting the causing exception's toString into the new exception, rather than the exception object itself, if it may be marshalled over a network connection to a host that doesn't have some specific exception subclass. Most usually, you never want to use the nested exception for anything except diagnostic output anyway, and for that only its string representation need be preserved.
In the specific case of a server, once I was successfully up, I would catch all throwables. I would do my best to report the problem, and try to struggle on. I would hope whatever system was down would come back up soon or whatever code did not work would not be used by the next caller. In this case, the difference between a NullPointerException
and a fileOpenException would be mute.
--- ITYM "moot"?
[Depending on your environment, staying up at all costs may be a very wrong thing to do. A common use of exceptions is not only to recover from errors, but to give a graceful failure path in the presence of non-recoverable ones. It's not hard to think of a circumstance where staying up at all costs could result in corrupted data, lost backups, etc. It's often better to come down (with an appropriate entry in the logfile, of course!) than to stay up no matter what. As a trivial example, Delphi applications often install global exception handlers that pop up an error message, and then the application *stays up*. One program I often use is very susceptible to data corruption and will end up throwing a huge series of error boxes as it tries to iterate over an invalid string, generating memory exceptions along the way, and refusing to just crash so I can try to fix it and bring it back up. -- ChrisMellon?
A checked exception seems to fall into a gap between normal failure, handled by the standard method signature, and unusual failure (out of memory, coding bug etc.) that is signalled by an unchecked exception and which cannot be handled. I have found the boundaries between normal failure, exceptional failure, and what cannot be handled at all, is unknowable by the author of a routine.
See also: GeneralizeOnExceptionBehavior
I think that checked exceptions can be used effectively - but not the way recommended by the received wisdom of the Java community. I've written this up in article form at http://www.octopull.demon.co.uk/java/ExceptionalJava.html
- which needs a bit of reconstruction before putting the arguments into Wiki form. -- AlanGriffiths
agrees that CheckedExceptionsAreOfDubiousValue
. -- NeilSwingler
Wow. This was a real eye-opener. I cannot believe so many people could think that the code possibly falling over is not a sufficient condition to warrant informing your callers. Amazing the number of comments supporting this article whose basic argument was "I don't wanna add a throws clause". You can lead a horse to water... -- RichardCordova
As long as both checked and unchecked exceptions are available, which they are in Java, I don't see what the big deal is. If you find that for your project's size and programming style
, the cost of refactoring due to changes in checked exception signatures is too great, then use unchecked exceptions instead.
(Of course sometimes an ill-advised use of checked exceptions occurs in a library you can't really change, as for SQLException in the 'Exceptional
Java' article cited above. But there are lots of other ways library APIs can be misdesigned that make this look fairly trivial.) -- DavidSarahHopwood
I have written a article summarizing the best practices on onjava.com. Visit it at http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html
. -- GunjanDoshi
I feel that a class of Exception should be checked or unchecked depending on the context in which the Exception occurs. A class of Exception is general type of problem, and the severity of the problem may vary based on the context in which it occurs.
Consider a couple examples ...
Typically, Java code is written such that a ClassCastException
should not occur, so it is reasonable that ClassCastException
is unchecked. However, when using reflection to dynamically load and instantiation a class that complies to some known interface, it might be preferable to propagate the ClassCastException
as a checked Exception. This could force the client code that supplied a bad class name to handle the problem.
- Example 2: MalformedURLException
A MalformedURLException could reasonably occur when constructing a URL based on input from an outside source, but if the input String is a hard-coded this should NOT occur. In the hard-coded case, it would be convenient to propagate the MalformedURLException as an unchecked Exception. If an unexpected problem occurs then the Exception is still propagated to the top-level error handler.
(Better examples are welcome.)
A method may even throw/propagate the same class of Exception as either checked or unchecked based on the exact situation. To allow for this capability two different forms of throw: "throwChecked" and "throwUnchecked" are needed. If a class of Exception is thrown checked or potentially propagated as checked then the method must be declared as throwing that Exception class. If an Exception is not caught then it propagates with its previous checked or unchecked status.
This approach to Exceptions would allow APIs to throw most Exceptions as checked, and then the client code could reduce the severity to unchecked as appropriate based on the context. This mechanism is still prone to abuse, but it is better than EmptyCatchClause
. This approach also facilitates LetExceptionsPropagate
without using a NestedException
; however, NestedException
still has its place for other cases of ConvertExceptions
on this idea.
Your own checked exceptions cannot propagate when you have to write classes that conform to somebody else's interface. See LetExceptionsPropagateOnlyAsUncheckedExceptions
for a strategy for using checked exceptions whilst allowing them to propagate freely.
I love checked exceptions. It's one of the reasons I prefer JavaLanguage
. And to make an actual contribution: what about CheckExceptionsAreDesignContracts?
In what way? The original Design Contracts are precisely unchecked exceptions, after all, so "what about" doesn't explain your thinking at all.
An exception is just a way for a function to deliver an alternative result (together with a special way to handle the alternate return). In fact, in programming languages that have syntactically lightweight tagged unions, you will often get a long way without special support for exceptions.
If an exception is just another return value, the question reduces to whether typed return values are of dubious value. You can read about this particular holy on numerous wiki pages and throughout the web, but ultimately you'll have to find your own answer. Mine is: don't bother with a monomorphic type system (such as Java's), but given parametric polymorphism, checked exceptions are nice.
Now I can understand why C++ has unchecked exceptions (the matter wasn't understood well enough back then), why Eiffel has checked exceptions (I think... everything's typed in Eiffel), but why Java choose to be wrong either way by implementing both
checked and unchecked exception is beyond my comprehension.
make no sense in the context of lazy or uninformed programmers hacking about. However, in the context of DesignByContract
(and admitting that contracts are the most important artifacts both in design, and implementation), CheckedExceptions?
play a fundamental role. The root of the problem lies in people's inability to differentiate between PreConditions?
and Errors. Errors can happen anywhere, and are generally rather unexpected. They are the result of coding mistakes, or systems or resource issues, and should be propagated with unchecked exceptions (i.e. Java's RuntimeException
, preferably wrapping the RootCause?
for later inspection).
, however, are "conditions that must hold, otherwise the service may
be refused. They do not result from technical considerations, but from (technology-neutral, preferably) design
considerations. In this spirit, it is absolutely imperative that the programming language forces the caller to make workflow decisions, should the service be refused for a particular reason.
promote the integrity of any system, *especially* during activities such as Refactoring. Unfortunately, seeing somebody understand and apply the semantics of DesignByContract
when they write Java Contracts are a rare occurrence indeed.
Checked exceptions aren't inherently worse than exceptions. But how early Java implemented them (requiring manifest declarations) made a serious issue for generic programming. For example, you could not write a generic 'map' function for lists without using some sort of painful ExceptionTunneling
pattern. I haven't paid attention to Java in years, so cannot say whether this issue has changed.
in general. A better alternative is to explicitly pass a handler object/policy, that way the caller can advise the callee how to proceed, without undoing any work. This is better than exceptions because it is both more flexible (e.g. one can add logging separately, inform a user or service to get advice, often resume computation), and more general (works well with concurrency or distribution, can escape a single stack, separates error policy from code which allows better reuse), and more secure (handler type is clear to the caller).
I typically find CheckedException
s extremely useful but only as long as you use them as an element of "what" and not an element of "how". CheckedExceptions?
represent the problems that are expected
to happen during the execution of the method. By expected I mean that they are part of the semantic
of the method. If your method is named openFile, then it is expected
to throw FileNotFoundException?
. If your method is named findProvider, then it is expected
to throw NoSuchProviderException?
. These should be made CheckedException
s as it forces the caller to deal with the problems that are inherent to the semantic of your methods.
This being said, you should never use a CheckedException
to expose any implementation detail that is not part of the semantic: if my findProvider method requires to read from a file, it should not
throw any IOException as this is an implementation detail.
My point is that an exception class in not fundamentally checked or unchecked. IOException can be used in both checked and unchecked scenario. Probably a better alternative would be that declared
exceptions should checked.