views:

599

answers:

10

Do you prefer checked exception handling like in Java or unchecked exception handling like in C# and why?

+13  A: 

Meh.

Checked exceptions are a great thing when used properly, but more often than not they lead to stuff like:

doSomething();
try
{
  somethingThrowsCheckedException();
}
catch(ThatCheckedException)
{ }
doSomethingElse();

And, frankly, that's just wrong. You should let exceptions you don't handle bubble up.

Checked exceptions used properly can be good. But very frequently, the result of doing checked exceptions properly is method signatures like this:

public void itMightThrow() throws Exception1, Exception2, Exception3, Exception4, // ...
Exception12, Exception13, /* ... */ Exception4499379874
{
  // body
}

Am I exaggerating? Only slightly.

Edit:

That said, one thing I prefer about C# over Java when it comes to exception handling has nothing to do with checked exceptions (I can get that if I go with Spec# anyway). No, what I like is that the stack trace in C# is populated when you throw an exception, rather than when you instantiate one as it is in Java.

Edit 2: This is for the commenters @Yishai, @Eddie, @Bill K:

First, you should check out this thread for information on how to get a stack trace without instantiating an exception. Keep in mind that walking the stack is a heavy process and should not be done on a regular basis.

Second, the reason I like C#'s exception stack trace being populated at throwal rather than at instantiation is that you can do things like this:

private MyException NewException(string message)
{
   MyException e = new MyException(message);
   Logger.LogException(message, e);
   return e;
}

// and elsewhere...
if(mustThrow)
{
   throw NewException("WHOOOOPSIEE!");
}

That's a trick you can't do in Java without having the NewException method included in the stack trace.

Randolpho
+1 the code block is not necessarily wrong, but often what happens in the catch{} is wrong wrong wrong
AdamRalph
Or what doesn't happen, in this case. Let me edit to expand on what I was trying to imply
Randolpho
You can write bad code in any language. At least in those cases you can immediately see the programmer is an idiot.
Tom Hawtin - tackline
@Tom Hawtin - tackline: Very true.
Randolpho
In C# how do you get the previous stack frame without throwing an exception (say if you want to get the name of the class that called this method)?
Yishai
@Yishai: Exactly. I've found it very useful diagnostically to say (with log4J): logger.warn("This shouldn't happen", new Exception("How did we get here")); where an Exception is never *thrown*, just instantiated, but that gives me the stack trace I need diagnostically.
Eddie
Although Checked exceptions are one of the few things I truly hate about Java because of the reasons you mentioned, I really like being able to get a stack trace without throwing an exception via the fairly short: "(new Exception()).printStackTrace();" (comes in handy a lot since I'm usually living in a java version 1.1 - 1.3 world. I suppose there is an easier way to do it in C#, so it's no big deal...
Bill K
@Yishai, @Eddie, @Bill K: Check out this question on how to get a stack trace without an exception: http://stackoverflow.com/questions/531695/how-to-print-the-current-stack-trace-in-net-without-any-exception
Randolpho
In java you would just do newException("WHOOOOPSIEE!").fillInStackTrace(); It returns throwable, so you can do it on throw line. I really see it as a Tomayto/Tomahto kind of thing.
Yishai
+6  A: 

I have never used Java, but since I read

I am quite sure I don't like checked exceptions (in the current implementation).

The two main points mentioned are the following.

Versionability

Anders Hejlsberg: Let's start with versioning, because the issues are pretty easy to see there. Let's say I create a method foo that declares it throws exceptions A, B, and C. In version two of foo, I want to add a bunch of features, and now foo might throw exception D. It is a breaking change for me to add D to the throws clause of that method, because existing caller of that method will almost certainly not handle that exception.

Adding a new exception to a throws clause in a new version breaks client code. It's like adding a method to an interface. After you publish an interface, it is for all practical purposes immutable, because any implementation of it might have the methods that you want to add in the next version. So you've got to create a new interface instead. Similarly with exceptions, you would either have to create a whole new method called foo2 that throws more exceptions, or you would have to catch exception D in the new foo, and transform the D into an A, B, or C.

Scalability

Anders Hejlsberg: The scalability issue is somewhat related to the versionability issue. In the small, checked exceptions are very enticing. With a little example, you can show that you've actually checked that you caught the FileNotFoundException, and isn't that great? Well, that's fine when you're just calling one API. The trouble begins when you start building big systems where you're talking to four or five different subsystems. Each subsystem throws four to ten exceptions. Now, each time you walk up the ladder of aggregation, you have this exponential hierarchy below you of exceptions you have to deal with. You end up having to declare 40 exceptions that you might throw. And once you aggregate that with another subsystem you've got 80 exceptions in your throws clause. It just balloons out of control.

In the large, checked exceptions become such an irritation that people completely circumvent the feature. They either say, "throws Exception," everywhere; or—and I can't tell you how many times I've seen this—they say, "try, da da da da da, catch curly curly." They think, "Oh I'll come back and deal with these empty catch clauses later," and then of course they never do. In those situations, checked exceptions have actually degraded the quality of the system in the large.

Daniel Brückner
So you're quite sure you don't like something you've never used, having seen one persuasive person argue it's "bad"? There are very reasonable arguments against both points above.
Eddie
Anders Hejlsberg doesn't argue that checked exceptions are bad - he states that they are a great idea in general (and I agree on that) but he argues they don't fit in all scenarios (with the current implementation). Hence, he argues, they should not be introduced because they will help in some scenarios but complicate other scenarios. What are the arguments against the above statements?
Daniel Brückner
+1  A: 

In practice it is better to use checked exception handling as it allows for more detailed information when your app begins flooding error logs at 2AM and you get a call to do some debugging...

Mr. Will
how do you mean?
jalf
I think he means that checked exceptions force you to consider how to handle exceptions, rather than the usual ignore them (which works until something goes wrong, and then you're up that creek).
Tom Hawtin - tackline
Thank you for clarifying my comment. If you use checked exceptions then you know you must handle X exceptions when calling certain methods. It adds another level of thinking when designing and writing code.
Mr. Will
+8  A: 

I prefer checked exceptions for things that can go wrong that you cannot predict ahead of time. For example, IOException, or SQLException. It tells the programmer that they have to account for something unpredictable going wrong, they cannot write robust code that will not throw an exception, no matter how much they try.

Too many times programmers view a checked exception as a language thing to handle. It isn't (or won't be in a well designed API) - it is an indication that there is unpredictable behavior inherent in the operation, and you should rely on a deterministic result of the operation always working the same given the same inputs.

That being said, in practice checked exceptions suffered from two things:

  1. Not all applications written in Java need that robustness. A compiler-level flag to turn off checked exceptions would be nice - although that could lead to APIs abusing checked exceptions when their developers work with the flag set to turn them off. After thinking about a better comprimise here, my current thinking is that a compiler warning is the best ballance here. If checked exceptions were compiler warnings, including a compiler warning if one was ignored several layers down (so the fact that one was ignored would be compiled into the class), so that the caller would at least know to catch Exception even if he couldn't know which one, then those who don't care would ignore the compiler warning, and those who do would not, without anyone being forced to write error handling code they don't care about to get their code to compile.
  2. Exception chaining took much too long (version 1.4) to introduce. The lack of exception chaining caused a lot of bad habits to develop early, instead of everyone just doing:

    throw new RuntimeException(e);

when they didn't know what to do.

Also, a checked exception is another API design element to potentially get wrong, and the users of the API have to suffer with the design flaw.

EDIT: Another answer points to two issues that motivated the C# design decision of no checked exceptions. To my mind, both of those arguments are very bad, so I thought they were worth addressing/counter balancing.

  1. Versioning. The argument is that if you change your API implementation and want to add additional checked exceptions, then you break existing client code.
  2. Scallability. Before you know it you have a method that throws 15 checked exceptions.

I think both versions suffer from the unaddressed point that when those remarks were made it was already accepted that the proper way to deal with a checked exception moving up to the next level would be by wrapping a different checked exception appropriate to the abstraction of your API. For example, if you have a storage API that could deal with IOException, SQLException, or XML related exceptions, a properly desgined API would hide those differences behind a general PersistanceException or something like that.

Besides that general design guidance, in the specific the arguments really lead to a lot of questions about the alternatives:

  1. Versioning. So a developer developed against your database API, thinking that they caught and handled the relevant exceptions (say DatabaseException) and then you decide in the next version to add a NetworkException to capture network level communication issues with the database. Now you just broke all compatability with existing code, and the compiler won't even complain about it. Everyone gets to discover it in regression testing, if they are lucky.
  2. Scalability. In the C# solution, if three API levels down there is a potential for access to a volatile resource, you are relying entirely on the API documentation, because the compiler won't tell you that.

That is a great design for web apps where dying and showing the user a nice error 500 page is about all anyone bothers doing (since transactions are handled by the container anyway). But not all applications are built with such requirements in mind.

The argument ends up boiling down (to me anyway): Don't worry about exceptions, anything can go wrong and just build a catch-all.

OK. That is the core difference between a checked and unchecked exception approach. The checked exception alerts the programmer to volatile unpredictable calls. The unchecked exception approach just assumes that all error conditions are of the same class, they just have different names, and they are made unchecked so that no one goes around catching them.

Now the arguments do have merit at the CLR level. I agree that all checked exceptions should be at the compiler level, not the runtime level.

Yishai
Exactly. Checked exceptions are all about the API just like interfaces. Interfaces have versioning issues too, but they are still indispensable
mcjabberz
+2  A: 

In my opinion there exist cases where checked exceptions are appropriate. There are probably features that could be done differently in Java to better support them. It isn't without difficulties (for instance, in some situations you might want an exception checked, in others not). Java does, of course, support unchecked exception types as well.

The sort of exceptions that are suitable to be checked should generally be documented. The best place to document is in the code. The populist approach is just to botch it and only consider the happy case.

Tom Hawtin - tackline
A: 

Checked exceptions are great as long as they are recoverable or not due to programming errors like an invalid index acces to a ResultSet. Otherwise they tend to pollute code layers and APIs by forcing the coder to declare things like IOException in many method signatures and giving nothing really useful to the client code.

svachon
+10  A: 

I think checked exceptions are a failed experiment. The original goal of exception was to eliminate the need to validate every function call return, which was resulting in programs programs that are difficult to read, and probably inefficient as well, thus discouraging programmers from signaling and handling exceptions.

While great on paper, in practice the checked exceptions reintroduced the very same problem exception were supposed to eliminate in the first place. They add a tightly coupling between the layers of application. They make it impossible for libraries to change their implementation in subsequent versions. The link posted by crausher goes into details and explain the problems much better than I ever could.

Remus Rusanu
+1 very well put.
Michael Borgwardt
I just don't get the coupling argument. Perhaps you can elaborate? If the exception is unchecked, client code could still be looking out for what is thrown and trying to catch it? Or is the argument that all catches should catch Exception and there shouldn't be any important difference between exception types?
Yishai
If component A uses component B and this in turn uses component C then A is exposed to the details of C simply because of the exception signatures, hence the coupling argument. Without this the use of C by B can be made transparent to A.
Remus Rusanu
B can wrap C's exception in its own thus making it transparent to A. Rethrowing introduces that coupling, I agree. Exposing your implementation's checked exceptions introduces coupling. But checked exceptions don't require you to do that. Without checked exceptions, then aren't you saying that A has to assume B can throw anything? So specific exception hierarchies become implementation details that callers should really ignore? If so, why allow subclassing of Exception at all?
Yishai
catch/wrap/throw we're back to square one, as each component has add code for every exception type, ie. code is becoming over complex for artificial syntax reasons. As for the specificity of exception, the problem exists no matter what. Your top level must have a last ditch catch-all case. The checked exceptions don't mitigate this by any means, they just force it down the throat of every function signature (ie. either add 'exception' to ur signature or face eternal maintenance purgatory).
Remus Rusanu
If B is told by the compiler that it has to wrap C's exceptions, or throw C's exception, it is an explicit dependency that the compiler forces you to think about. It would seem that your prefered design is that all exceptions be caught at the catch-all, and that nothing else care. Is that an appropriate decision to make at the language level? In my project I can always make it by wrapping it in an unchecked exception as soon as I see a checked exception.
Yishai
A: 

I think in most cases checked exception are a waste of time. They entrap to things like the antipattern mentioned by randolpho or extensive creation of custom exceptions to decouple your implementation from used libraries. Beeing rid of this 'feature' lets you focus on what you want to do.

zoidbeck
+3  A: 

OK, I wasn't going to reply, but this is taking too long to get closed and got many answers on one side of the fence, so I feel the need to weigh in on the other side.

I support checked exceptions -- when properly used -- and believe that they are a Good Thing. I've heard all of the arguments above many times, and there is some merit in some of the arguments against checked exceptions. But on net, I think they are positive. Having programmed in C# and Java, both, I find C# programs are more difficult to make stable against Exceptions. The great thing about checked exceptions is that the JavaDoc is guaranteed to tell you that the Exceptions can be thrown from that method. With C#, you rely on the coder to remember to tell you what exceptions may be thrown from any given method, and also what exceptions may be thrown from any method called by that method, and so on.

If you want to create 5-9's reliable code, you need to know what exceptions can be thrown from code that you call, so you can reason about what can be recovered from and what must cause you to abandon what you are doing. If C#, you can do this, but it involves a lot of trial and error until you have seen all of the possible exceptions that can be thrown. Or you just catch Exception and do your best.

There are pros and cons to both approaches, that of Java and C#. Reasoned arguments can be made in favor of both, and against both. Again, on net, I prefer the approach chosen by Java, but were I to re-write Java today, I would change the APIs to change some checked exceptions into runtime exceptions. The Java API is not consistent in its use of checked exceptions. And as someone else said, it took far too long for Exception chaining to appear as a standard API feature and a part of the JVM.

However, the charges that are lain at the feet of checked exceptions too often fall into the category of, "lazy programmers misuse this language feature." That's true. But that's true of many languages and their features. The "lazy programmer" argument is a weak one.

Let's address the main complaints that don't fall into the "lazy programmer" bucket:

  1. Versionability - yes, throwing a new Exception in a new version of your code will break compilation for clients who blindly drop in your new JAR file. IMO, this is a good thing (as long as you have a good reason for throwing an additional checked exception), because the clients of your library have to reason about what they need to do with this behavior change. If everything is unchecked, then your clients don't necessarily have any clue (until an Exception occurs) that your behavior has changed. If you are changing the behavior of your code, then it's reasonable for your clients to have to know about this. Have you ever updated to a new version of a 3rd party library only to find its behavior has invisibly changed and now your program is broken? If you make a breaking behavior change in your library, you should break automatic compatibility with clients using earlier versions of your library.

  2. Scalability - If you handle checked exceptions properly by translating them to a specific checked (or unchecked) exception appropriate to your layer of the API, this becomes a non-issue. That is, if you code properly, this problem disappears. And doing so, you properly hide your implementation details, which your callers shouldn't care about anyway.

Too often, this is simply a religious issue with people, and that's why I get (unnecessarily, I know) irritated. If you have a religious aversion to checked exceptions, that's OK. If you have a reasoned argument against checked exceptions, that's OK. I've seen good reasoned arguments (that I mostly don't agree with, but still...). But mostly I see bad arguments against checked exceptions, including arguments which were fair and reasonable when talking about Java 1.0, but which are no longer applicable given modern releases of Java.

Eddie
A: 

The only thing I'dd like the compiler to check for me is whether a function throws exceptions or not. What specific exceptions that can be thrown doesn't matter. The thing is that in my experience, there are a lot functions that don't throw anything, and it would be nice if this was documented in the function specification. For these functions you don't have to worry about exception handling.

Dimitri C.