
|
Exception debate among other industry "names"...
Great topic Joel, and one on which I've been doing a lot of reading on among some popular names in the programming industry -- Bruce Eckel, Anders Hejlsberg, and James Gosling.
-To start: Bruce Eckel's view on checked Exceptions:
http://www.mindview.net/Etc/Discussions/CheckedExceptions
Bruce basically says that since compile-time exception checking fails to check many exceptions anyway, we should move more towards a system where exception checking becomes a runtime issue, rather than being dictated to programmers at compile time. So the problem breaks down to a compile-time vs a run-time issue.
-Anders Hejlsberg's (lead designer of C#) view on checked Exceptions:
http://www.artima.com/intv/handcuffs.html
Basically says that programmers shouldn't be forced to check exceptions because they needlessly complicate code. Exceptions should be raised, but C# is silent on how programmers should deal with them because a good checked exception framework hasn't been invented yet (in the opinion of C# designers).
However, Hejlsberg does say that Exception handling should be centralized (he gives some good reasons), whicch goes against Joel's philosophy of immediately writing the equivalent of catch blocks around every possible exception a function may throw.
-James Gosling's (he needs no intro) view on checked Exceptions:
http://www.artima.com/intv/solid.html
Gosling believes forcing programmers to *intentionally* ignore error conditions forces them to at least deal with potential errors. He also says that having one central exception handling location (in the sense that Anders was proposing) really only gives one the option of rolling over and dying, rather than recovering gracefully.
Joe makes two points about Exceptions, neither of them completely correct in my view.
"1. They are invisible in the source code. Looking at a block of code, including functions which may or may not throw exceptions, there is no way to see which exceptions might be thrown and from where. This means that even careful code inspection doesn't reveal potential bugs. "
In Java at least, this is wrong. The compiler will warn you if you aren't checking all possible exceptions. When reading code, simply look at your catch blocks to see what kinds of exceptions may be thrown. The only way not to see exceptions is if the programmers swallows them or lazily rethrows a new Exception object. In other words, programmers have to go out of their way to make them invisible.
In nested fucntions, you are assured that possible Exceptions have been explicitly handles since the compiler won't let you ignore them.
And in the end, code inspection, no matter how careful, will ever reveal all potential bugs. Unit testing does a better job of this.
"2. They create too many possible exit points for a function. To write correct code, you really have to think about every possible code path through your function. Every time you call a function that can raise an exception and don't catch it on the spot, you create opportunities for surprise bugs caused by functions that terminated abruptly, leaving data in an inconsistent state, or other code paths that you didn't think about. "
Again, I have to stress that in Java, it's impossible to call a function that raises an exception without handling it on the spot. You have to deliberately write code to ignore the exception if you wish to ignore it. But you can't conveniently forget about it.
In C++ the situation is murkey since you have two ways of dealing with exceptions (function return values and the new Exception handling facilities).
One way around avoiding having things in inconsistent states after an Exception is raised in a called function/method is to use a "finally" clause. Then you can exit gracefully after whatever cleanup tasks have been completed.
-My lame conclusions
Overall, I agree with Anders' belief that handling exceptions for each function call seperately (as Joel proposes) can needlessly complicate code and make later refactoring extremely difficult. However, often times, I too wonder if Java's infrastructure of handling exceptions is the correct way.
Crimson
Tuesday, October 14, 2003
Would Java be fun if it supported both a catch and a finally? Variations could be finallies (sp?) that run before the catch, and ones that run after the catch.
But what happens to exceptions thrown in the finallies themselves?
(btw, doesn't Java also have some exceptions that aren't on the throw line.. forget the term for it, but usually fairly fatal system errors that you couldn't be expected to catch).
i like i
Tuesday, October 14, 2003
i like i: java does have 'finally', your block of code looks like
try {
<blah>
} catch(SomeException e1) {
<blah>
} catch(SomeOtherException e2) {
<blah>
} finally {
<blah>
}
There's nothing special about the code in the 'finally' bloke, if it throws an exception, the exception is thrown as you would expect.
Konrad
Tuesday, October 14, 2003
"invisible in the source code"
Untrue in C++, as well as Java. MSDN says this: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrfexceptionspecifications.asp on the subject. I think specifications are not supported in vc6, someone can correct me if I am wrong.
Here is a pointer as to why exceptions can be A Good Thing and in what situations they can be so: http://www.parashift.com/c++-faq-lite/exceptions.html
Tuesday, October 14, 2003
Joel, as a long time avid reader of your site I really like most of what you write and agree with most of your views.
Alas, this time you are sooooo wrong. This article of yours just displays that you haven't yet understood how to use exceptions. It personnaly took me some years before I understood how to use them, but believe me, when you get around to it it's the best thing since sliced bread.
Contrary to your comment about having to sprinkle catch clauses all ove the place, the crucial thing to understand about exceptions is that you should never catch an exception that you don't know how to handle, and most of the time, the current function cannot do anything about it.
It all boils down to allmost only catching exceptions in a central place (like main i c++). Once you realize that, life becomes so much simpler.
But there are exceptions of course... :)
Frederic Gos
Tuesday, October 14, 2003
Unfortunately missed exceptions in Java has been the norm for me (not my own code, but in writing code on other people's libraries).
Two problems exist.
1) Runtime exceptions.
"RuntimeException is the superclass of those exceptions that can be thrown during the normal operation of the Java Virtual Machine.
A method is not required to declare in its throws clause any subclasses of RuntimeException that might be thrown during the execution of the method but not caught."
There still exist those pesky runtime exceptions like divide by zero exceptions. This is further complicated by the fact that a user can choose to extend RuntimeException, and throw their own RuntimeExceptions thus not requiring a catch clause. I know this isn't good programming practice, but who said all programmers were good?
2) Serialized Exceptions
The Java compiler is capable of checking errors that only arrise in the local code. In a client/server architecture, I have many times run across the problem where the client throws an error, which is then passed back to the server. Even if there is a generic "Catch(Exception e)" clause, the server can still possibly crash if it doesn't contain the codebase for the error that came back.
For example, the "ClientIsBrokenException" definition exists only on the client. If for some reason, the client passes this exception back to the server, the server chokes.
Thus has been my experience, but I don't claim to be a Java expert by any means. Just comments illustrating there is a lot more to exceptions in Java than simply letting the compiler type check things for you.
Elephant
Tuesday, October 14, 2003
Just a comment on runtime exceptions & java.
I am fortunate to work on a mature Java product (on it's 5th release or so) and only recently have we come to terms with a decent exception strategy. One of the lightbulbs that went off in our heads recently is that the runtime exception should be treated as a programming error, and they should be try/caught strategically throughout the code. The best example I can think of is when we cede control to a listener in an observer pattern, we try / catch runtime exception because we don't trust observers. I don't think there is an elegant way to do that with errors & return codes. (although there are always ways) We leaving catching Exception to a higher level, and then throwable in the main method.
This has led to a significant increase in the robustness of our code, and we get comments from customers all the time about how 'stable' our code has become. I think with properly managed exception hierarchies you can create a very good robustness story, which is very important to our customers.
One last point, in our program over 2/3 of the work we do is handling the error paths, not the success paths. I think the extra work you incur by handling exceptions is representative of the code you really should write in the first place.
I would have to respectfully disagree with Joel this time, the first time I can remember thinking that in some time ...
John
Tuesday, October 14, 2003
One quick point.
Elephant mentions how Java programmers can extend RuntimeException and thus bypass the need to catch them (they don't require explicit declaration or handling). Doing this solely to avoid having to handle errors is bad practice, but, he says, not all programmers are good.
Easy response to this. If programmers aren't good, then exceptions are by far the smaller problem. Who knows what other corners they're cutting?
I'll chime in with others and say I didn't know what Joel was thinking in his article, but perhaps it's because he's dealt more with exceptions in languages other than Java. I've found Java exceptions to be quite helpful in maintaining robustness. I have also witnessed cases where they were grossly abused. (I was seeing methods declared to throw Throwable(!), because the coder couldn't be bothered to enumerate exactly what could be thrown.) Naturally, anything's abusable if you work at it enough.
Paul Brinkley
Tuesday, October 14, 2003
I have to chime in and agree with the other commenters in saying that Java exceptions are preferable to the return code solution you're proposing Joel. I remember avoiding C++ exceptions like the plague but in the years of working with Java I've really come to appreciate them.
Golden Rule: You can't legislate for stupid programmers or save them from themselves. The best you can hope for is to provide a language / platform that doesn't lead good programmers down the wrong path and makes their life easier not more difficult.
Exception management is an architectural issue as much as a development / implementation issue. Firstly, you've got to be clear on the difference in Java between the various Exception base classes, have good practices on declaring exceptions and handling them.
One particular powerful aspect is the ability to centrally manage (or delegate) exception handling. For example, someone using Struts framework for a web application can have all database connectivity exceptions handled in a similar fashion in one block of code.
Exceptions being classes you get all the benefits of OOP with your error statuses which means extensibility, polymorphism etc etc
Exception handling is cleaner to code than error code handling but it requires a clear strategy and controlled execution. Where a lot of poorly built Java apps go wrong is:
* Not having a root exception handler to gracefully handle programmer or system failures without alarming the user.
* Hiding exceptions by swallowing them or throwing a new exception without the trace of the original exception.
* Not declaring exceptions properly, ie. the most specific exception thrown by the method
* Handling the exception too early when you can't really do much about it (e.g. a complete network failure)
* Not handling an exception when you can do something about it
* Using RuntimeException when you don't want the application to exit in that situation. The only valid time to use a RuntimeException is when it is a fatal / abort signal to the system and even then in multi-user or multithreaded environments there are reasons to catch such exceptions at the entry point of the thread.
* Less of a sin although annoying is poor Exception messages.
I'm sure I've missed a few but compare that list to the many, many issues with return code handling and it really doesn't seem you're on the ball with this. Have you seen some of the spagetti code that comes with error code handling? Or how fragile it can be as the application develops? Or how limited your options are in delegation or how often poor error code reporting slips past the initial development stage? Or worse yet, when dealing with APIs that have poor error code reporting in parts of their system?
My previous experience with various C based database drivers makes me shiver.
Seyed Razavi
Tuesday, October 14, 2003
The best discussion of exception I've ever seen (industry luminary or otherwise) is the one Bertrand Meyer gives in his book, "Object-Oriented Software Construction"
http://www.amazon.com/exec/obidos/asin/0136291554
There is also a nice summary on the Eiffel web site:
http://archive.eiffel.com/doc/online/eiffel50/intro/language/tutorial-09.html#pgfId-514761
Meyer explains compellingly the need for and proper implementation of exceptions. Well worth reading even if you find exceptions ghastly, as Joel seems to.
DRP
Tuesday, October 14, 2003
Joel, have you ever done any extensive Java programming?
I usually find Joel's articles very entertaining and interesting. Unfortunately I also have to add my criticisms about Joel's exception article. I think his comments about the Java Exceptions indicate a lack of understanding of the Java platform. C++ is well know for problems with its exception framework and I think Joel has erroneously lumped Java in that basket.
Java has 2 exception types: Checked Exceptions and Runtime Exceptions. It also has Runtime Errors.
Checked Exceptions are declared in a method signature. They are application or library specific exceptions which provide a context to any exceptional conditions which *are known at development time* that can occur. When a method with a declared checked exception is used, the compiler ensures that the exception is handled by the calling code. It is very visible in the code and the documentation. The exception framework allows the caller to act appropriately when an exception occurs or else throw the exception to a higher level in the call stack where it can be handled intelligently. Bad programmers can abuse this facility by swallowing exceptions (re-throwing a java.lang.Exception up the stack in place of the specific caught exception such as java.io.IOException). Programs are intended to recover from checked exceptions. An example is when a GUI file dialog tries to open a file which no longer exists. An IOException is thrown which can be caught at the appropriate level in the application to allow an alert to be displayed to the user informing them of the problem and asking them to try again. Code Reviews should stop any misuse of checked exceptions. There is never a reason (except laziness) to misuse checked exceptions.
Runtime Exceptions on the other hand are not application or library specific. What I mean by this is that their purpose is to react to programming or bad data errors *which were not expected to occur*. These type of exceptions are not checked at compile time because of that reason. I actually believe Joel must have been talking about this class of exception because they are infact almost invisible. An example is a divide by zero exception or an array index out of bounds exception. These are programming or data bugs which were not supposed to occur. If the application was of a nature where you would expect data problems which may give you a divide by zero error you should be aware of this at design time and you would specifically attempt to catch and deal with this exception. However in general you would not want to have specific code to check *every* calculation you ever write for divide by zero errors. It is this simple, they are design for use in the case when you have decided you are not prone to these exceptional conditions. If you know you are then you are prepared for them and catch them. If you know you are not then you shouldn't have to catch them and so you would not put the catch blocks in your code. The case for writing your own Runtime Exceptions is not a strong one. Their use should be restricted and a checked exception should always be favoured. Again code reviews can stop misuse of these exceptions.
java.lang.Errors are the final throwable object. These relate to the java platform system errors like OutOfMemory. These are runtime and not checked by the compiler. In this case there is very little you can do, its a bit like a core dump in C!
Joel, I can't help but feel a little bit of your credibility fading away when you talk about technical issues which appear out of your areas of expertise. I hope that you will look at this area again and come to the conclusion that Java exceptions are not all that bad and a hell of lot better than spaghetti switch statements testing error values.
Donal McCarthy
Wednesday, October 15, 2003
Recent Topics
Fog Creek Home
|