 |
Things that could be different - Part 1: Exceptions
Posted by mister__m on January 07, 2004 at 12:53 PM | Comments (6)
Exceptions are a new concept for most people when they get to learn Java. Even though C++ offers some degree of support for them, a number of C++ programmers never heard of exceptions since the language they were used to did not force them to handle or declare exceptions. Other languages are said to have them as well - such as Ada, though I just read this information a few times and know nothing about it -, but no language addresses the issue the same way Java does.
Checked exceptions are a great idea because they force you to accept the fact things can actually go wrong. The more skilled we are, the more we tend to think our code is (almost) perfect and that there is absolutely nothing that could possibly go wrong in that small block we are working on now. Exceptions work as a remainder that something in the environment may not be properly configured and help us to remember the fact that, as humans, we do write code that does not work - and that we always will, though good programmers tend to write it less often. That being said, let's move to the most relevant part of this entry :-D.
So, let's think about it about the way exceptions were designed. To begin with, why does RuntimeException descend from Exception? Lots of frameworks and APIs - EJBs, for example - assume that RuntimeExceptions are system errors that they should handle in a special way - by discarding the instance that has thrown it and rolling back any pending transactions, for instance. So, if you, as programmer, for any reason, decides to write code like that:
try {
...
} catch (Exception e) {
//do something and...
throw new BusinessException(e);
}
while using these technologies, you'll prevent RuntimeExceptions from being propagated to the upper calling context while maintaining their proper meaning, probably resulting in something entirely different from what you meant. If you need to write code like above - ok, it is a polemical issue, but sometimes it is indeed needed -, you have to catch RuntimeException before catching Exception and rethrow it. What if it was the opposite? What if Exception descended from RuntimeException? Or even better: what if Java had a Exception class with two subclasses: CheckedException and UncheckedException?
That would make exception handling more powerful, as you would be able to handle just checked exceptions (CheckedException), just unchecked exceptions (UncheckedException), all the exceptions (Exception), just errors (Error) or everything that could be thrown (Throwable), without having to remember to "exclude" any type of exception you didn't want to handle.
Another thing that could (should) be different is the inheritance hierarchy. Before JDK 1.4, for example, there was no support for exception chaining. So, lots of developers out there rolled their own solution for this problem at that time. And it wasn't clean or nice, actually. Why? The problem is that there is no easy way to extend the exception system because it relies on inheritance.
People wanted to add a cause to an exception so they could wrap the "real"exception inside a reasonable exception for each application layer. How could it be made? It would be nice if you could just create a subclass of Throwable or (not so good but acceptable) if you could add this behaviour in a subclass of Throwable and then write subclasses of Exception, Error and RuntimeException that inherited from your Throwable subclass, right? But that was not possible, unfortunately. That would require multiple inheritance. If you wanted to implement this simple change, you had to write at least three subclasses - one for each of Exception, Error and RuntimeException - and all of them had to have the same methods declared and implemented. If you wanted to avoid repetition, you had to design a fourth class just to hold the Throwable that caused the exception and provide a getter and a setter, but you still had to implement those methods in your three subclasses and keep a reference to an instance of your fourth class in each of them. Awkward, ugly, terrible are not strong enough to describe this solution.
How could this problem be solved? Actually, that's a pretty complex question with many answers. Exceptions could be based mostly on interfaces and just use one superclass. We could have an Exceptionable class we would have to inherit from, and have three interfaces that would allow a class to be treated as a checked exception, an unchecked exception or an error. Or maybe the throw clause could be different, allowing us to do:
throw e as UncheckedException;
, which would gives us even more flexibility because we would be able to throw the same exception instance as either a checked or an unchecked exception in different contexts. Obviously, we'd still want to have a superclass we were forced to extend in order to be a valid parameter to the throw clause; otherwise, Integers could be treated as valid throwable instances - something ugly, but allowed by C++. There are many other solutions, but the main point is a class hierarchy as we have today is not the best way to make throwable objects being handled differently.
I would like to point out, though, that it is very easy to speak about something that was conceived almost 10 years ago, after many years have passed and after using it and seeing others using it; it's a huge advantage the original creators couldn't have. James Gosling and all the other folks at Sun have made a great job designing Java and its API and, after nearly a decade, it is obvious there are things that could be better. I'll write more about other things - some far more critical than the way exceptions work - soon. Stay tuned!
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
Good ideas
I like your ideas, especially CheckedException and UncheckedException, and the "throw as" construct. It kind of makes sense for RuntimeException to extend Exception, but it would kind of make sense the other way around, too. I think it would be better if neither extended the other.
Posted by: keithkml on January 07, 2004 at 01:59 PM
-
Re: Objection Mr Santos, leading the witness
I was expecting to get bad comments about this entry, mainly because people wouldn't agree with some of my thoughts about the issue. But I am somewhat suprised someone is making some noise because of one code sample - which, apparently, the person didn't get the point.
So, here is the answer I gave Jon Eaves about his blog entry:
Hi Jon,
First of all, I don't think you've read my blog properly. Note my own comment about this code:
If you need to write code like above - ok, it is a polemical issue, but sometimes it is indeed needed -
So, as I said, sometimes it is neeeded. Do you want an example? I'll give you one.
Let's suppose you are writing a SQL Driver implementation. What do you want to do with any Exception that is thrown by your internal code? Don't you wanna wrap it inside a SQLException? OK, maybe you don't want to do that with RuntimeExceptions because probably, after some testing, you reach the conclusion they only can happen if the user do wrong things with the API. Would it be wrong if, inside your code, you wrote a block that had catch(Exception) and then wrapped it inside a SQLException and rethrew it? I don't think so. That is an example of what I was talking about.
Prevayler - http://www.prevayler.org/ - , a terrific idea for object prevalence, also contains similar code, but they catch RuntimeException before catching Exception for obvious reasons. It is a necessary thing in some situations, although the vast majority of uses of this block just happens because people cannot understand good exception handling or are just too lazy.
Notice I never told anyone this kind of code is the way you should handle exceptions all the time.
Thanks for reading my blog ;-)
Posted by: mister__m on January 08, 2004 at 05:57 AM
-
RuntimeException
"RuntimeException" has always struck me as a silly name, since all exceptions occur at runtime. Your suggestion of CheckedException and UncheckedException is much better To support that, I'd suggest that their proposed base class, Exception, be made abstract (along with Throwable). After, what is an exception is it is neither checked nor unchecked?
I also like the idea of "throw e as Checked/UncheckedException," but I'm not sure how that would work with javac. Right now, of course, the compile determines whether an exception must be declared or caught based on its type. But if an exception could be either checked or unchecked, that would no longer work. Add interfaces, abstract classes, and polymorphism, and you've got a real mess.
For example, A extends B. A.method() throws an exception as a UncheckedException. B.method() overrides A.method() but throws the same exception as a CheckedException. However, this would be valid code:
A a = new A();
a.method();
A b = new B();
b.method();
In the last line, must we declare or catch the exception? How would the compiler determine this?
Posted by: jimothy on January 08, 2004 at 02:10 PM
-
Re: Objection Mr Santos, leading the witness
> I was expecting to get bad comments about this entry, mainly
> because people wouldn't agree with some of my thoughts about
> the issue. But I am somewhat suprised someone is making
> some noise because of one code sample - which, apparently,
> the person didn't get the point.
I'd like to disagree here... jon is pretty blunt in his reply, but my impression is that his complaining makes a lot of sense.
I do like Java's exception handling mechanism. I don't see why the "CheckedException" and "UncheckedException" proposal would bring any benefit above of what we have.
You say that "The main thing is that That would make exception handling more powerful", but also says that "the vast majority of uses of this block just happens because people cannot understand good exception handling or are just too lazy".
So, you basically negates the value of you "main thing"...
If the vast majority of uses are for non-valid reasons, it basically means that the vast _correct_ uses just catch the necessary exceptions, and then the "CheckedException" and "UncheckedException" idea is irelevant.
I think that the javadoc has a great definition: "The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch."
Checked exceptions are specific. You know you have to deal with then even before you start writing your exception handling code. the compiler will actually list then to you. Creating "Checked/UncheckedException" adds no value to that, but encourages more of the "I'll just put this generic Exception handling code here" type of attitude that you say yourself is reasonable in only a few cases.
So, I do agree with jon. If you use a bad example to prove your point then you prove nothing. Come up with a better situation were handling "CheckedException" would be the way to go, and that would help make your point.
In any case, everything always could be different, and discussing possibilities help us better appreciate what we have. I'll be looking forward for the other "far more critical" issues. Tks!
Posted by: brunos on January 08, 2004 at 03:54 PM
-
Re: RuntimeException
>To support that, I'd suggest that their proposed base class, Exception, be made abstract (along with Throwable).
> After, what is an exception is it is neither checked nor unchecked?
I totally agree. I think that - whether it's called Exception or Exceptionable - combined with interfaces or a throw as construction would make exception support far more powerful tha it is now.
About your second question - about how javac would be able to figure it out -, it's the same as today: you cannot change the throws clause of a method, right? And that does not include RuntimeExceptions today right? That's exactly the same way javac could work with throw as constructions. If you throw something as a CheckedException, you must declare it. If your superclass didn't declare it in the method signature, you can't do it. Hope things are clearer now.
Posted by: mister__m on January 08, 2004 at 05:35 PM
-
Re: Objection Mr Santos, leading the witness
OK, now I see Mr. Eaves isn't alone :-D
But let's get to the point. First of all, as I said, I like checked exceptions. I like the exception handling mechanism, just think it could be better.
The fact most people use it wrongly doesn't mean it is not useful. I haven't even said they use in a bad manner all the time; I just said that most people use it for something they shouldn't.
Let me explain it with an example. Prevayler - again, if you don't know it, do it! :-D - handles Exceptions in its own code with two "catches": one for RuntimeException, the other, Exception. Your custom class is declared to throw Exception in its execute() method - the same happens to Action in Struts. Why? I'll tell you why: the framework cannot predict what exception you want to throw, but it "knows" you may want to throw any exception. So, they really have to catch Exception either for rethrowing or for using reflection to get the right "exception handler class", method, jsp or whatever they use for handling exception in the end of the day.
I hope this helps you to understand what I meant. Putting it in a simple way: generally, catching (Runtime)Exception is likely to be a good thing if you are writing a framework or if all the exceptions are going to be handled dinamically in upper contexts. There, they'll be checked for specific types and specific actions will be taken - or not; in a web application, there a lot of exceptions that should be handled the same way, even though their only "similarity" is they descend from Exception. Apart from those cases - even if you are writing a framework, there a lot of times you shouldn't catch Exception -, it's a silly mistake. Most folks are writing applications, not frameworks, and they are not handling exceptions anywhere - they silently killing them -, so they are wrong.
Just to show an example that, even if people misuse it, it should get better: reflection. I've seen developers using reflections for many stupid - sorry, but that's the word - reasons. Some don't know they should use interfaces, others think that just because "reflection cost is nothing" they can build layers and layers of code that uses it in - I've seen it, be prepared! - finantial transaction handling, even though there are hundreds of thousands of them happening and they need to be handled asap. Does it mean we shouldn't change it so it's better for the ones who are using it properly - there are a lot out there, thanks God -, even though people could misuse it easier? Don't think so. Things should be improved, even though most people will do bad things with them. As they say, innovation has its own costs.
Posted by: mister__m on January 08, 2004 at 05:55 PM
|