Fog Creek Software
Discussion Board




Exception Handling - Java vs .NET

When I began working with .NET, one thing that struck me as odd was that a class which threw a specific error was not required to specify that it threw it, nor were enclosing classes required to catch it. This is different to me from the Java world where if you are using, say, an IO class, you have to be able to catch the IOException it could throw.

To me this means that when you are working on a large project with .NET you can't count on your exceptions being caught or handled at all.

I was thinking to myself this morning that perhaps it wasn't so odd, and was wondering what you JOS'ers thought about the difference in exception handling.

CF
Tuesday, July 06, 2004

Exceptions propogate to WinMain() which is just hidden by the VB or C# equivalents.

int WinAPI WinMain(HINSTANCE..)
{
  HandledWinMain(hInstance...);
}

int WINAPI HandledWinMain(HINSTANCE)
{
  try {
    while (GetMessage())
    {
    }
  }
  catch(...)
  {

  }
}

I'm a jackass
Tuesday, July 06, 2004

There's a big discussion of why this is so somewhere on the Microsoft website, among their design of C# interviews.

What it boils down to is this:

Throw specifiers (in the opinion of C#'s designers) impose an unreasonable overhead in terms of extending existing code and an unreasonable dependency of code on what it calls and what inherits from it.

I have to say I found their arguments compelling, but then I have no real experience of throw specifiers. Although they exist in C++ their implementation is such that they are practically useless if you ever interface with any libraries.

Mr Jack
Tuesday, July 06, 2004

Actually, if you were to be pedantic, Java also has exceptions that don't  need to be caught, or unchecked exceptions (see RunTimeException)

Making all exceptions unchecked was a conscious decision by Anders Hejlsberg, and he mentions his justifications in the following interview:
http://www.artima.com/intv/handcuffs.html

I don't necessarily agree with his reasoning but I understand & respect his thoughts on the matter. BTW, Bruce Eckel, the interviewer and author of Thinking in Java, is also against checked exceptions as well.

SC
Tuesday, July 06, 2004

BTW, I program in both Java and C#. However, I see the argument between checked and unchecked exceptions being reminiscent of Joel's argument against Custom Fields in FogBugz.

Ultimately, it's a philosophical decision on whether the language should insulate you against bad habits.

SC
Tuesday, July 06, 2004

SC: Thanks for the article. It was very interesting, though I agree with you that some of his points weren't good arguments. Saying that checked exceptions are bad because most programmers are lazy and can't be bothered with catching them doesn't seem like a good argument IMHO.

He also seems to contradict himself a bit - he says to make things as simple as possible, but then talks about creating a class that has 40 checked exceptions because of all the various classes it inherits from.

It seems to me that if checked exceptions were created properly - using an inheritance chain - then handling them shouldn't be as difficult in your calling code. For example, if I am inheriting from a class which can throw a File not Found, a File Read Only, and things like Array Out of Bounds, etc, I am violating design patterns in which you should only do one thing in your classes.

Perhaps you are right that it is a philosophocal question, but it just seems like you can write better code if you know what in the world the classes above you are going to do. Otherwise you end up just doing a try/catch Exception which defeats the whole purpose of throwing well-defined exceptions.

CF
Tuesday, July 06, 2004

Speaking of this, lately I've been using FxCop to validate my .NET code per Microsoft standards, and it contantly bitches about my use of

try
{
  // do something to get a value, for instance
}
catch (Exception)
{
  // set the value to the default
}

I do this ultimately because I have no clue what specialized exception this particular class throws, and I don't really care to do trial and error just to learn it. Is there any documention or method to know what normal exceptions may result from an operation?

.
Tuesday, July 06, 2004

"Is there any documention or method to know what normal exceptions may result from an operation?"

., this is one of my biggest complaints about .NET's approach to exceptions - the libraries I've seen are poorly-documented with respect to exceptions.  I can deal with the argument that exception-handling shouldn't be compiler-enforced (though I prefer Java's approach), but when the exceptions for the stock .NET library classes aren't documented at all, I think the argument falls apart.

The only approach I've seen that works here is decompiling (google for anakrino), although that can be painful if the codepath goes through many different methods/classes.

schmoe
Tuesday, July 06, 2004

I agree with . and schmoe...  It'd be *really* nice if VS.NET intellisense had some way of showing you what exceptions a method call could throw.

I did find it a pain to declare all my exceptions w/ "throws" in Java, but I'm not sure the .NET way is any better since it just leaves me digging through sparse documentation.

In the end, I also end up resorting to catch(Exception ex) style quite a bit.  In certain situations, I think it's perfectly appropriate to insulate the users of your code from its inner workings.  That's what OO is all about, right?

Of course, if you're swallowing exceptions that you didn't really know could/would occur, that can cause strange behavior...but sometimes it's enough just to know the operation failed, log the message & stack trace, and get on with life without some ugly melt down right in the user's face.  None of which requires knowing what specific subclass of Exception you are catching.

Perhaps what we really need in .NET is a "throws" keyword and a project/assembly-level compiler option specifying whether or not to enforce it.  Or would that be giving us lazy programmers too much choice? ;-)

Joe
Tuesday, July 06, 2004

"Perhaps what we really need in .NET is a "throws" keyword and a project/assembly-level compiler option specifying whether or not to enforce it. "

That is exactly what I would look for that would make me happy. Unfortunately it would probably have to be enforced all the way up the stack, and hence, since Microsoft themselves didn't use it, would cause most everything to break. But I wouldn't mind having a choice in the matter, kind of like compiling with the Strict option.

CF
Tuesday, July 06, 2004

Sure, except that (as the interview with Anders says) a solution like this would break because of polymorphism and versioning.  What exceptions does a virtual method throw?  When compiling with "strict", either the virtual method throws all possible exceptions, or else you could be missing exceptions.  And what happens if you always compile with "strict", but compile a newer version that throws more exceptions?  What happens to the callers?

Unfortunately, the split really needs to be there - in Java, the checked exceptions thrown by a method are a part of its signature, so a subclass or interface implementor can't throw new exceptions.  Either you live with this limitation or you don't - either one has its own costs.

schmoe
Tuesday, July 06, 2004

I'm by no means a language/compiler designer, but I'm not sure what the difficulty with virtual methods would be.  A virtual method, or an interface implementation method, would automatically inherit the throws clause of its base.  Additionally, virtual methods would be able to override the throws clause (although only in its entirety).

If you add exceptions to the signature, the callers must be updated to handle them.  Like any part of the method signature of a public-facing API, it should be carefully considered and only modified when absolutely necessary.

Also, just because you compile your assembly with "Option Strict Exceptions" doesn't mean that the users of your API also need to compile their apps with it.

Furthermore, the strict exception setting should be able to have a warning level attached to it, so that you have a choice of whether to generate a full error or just a warning at compile time.

Joe
Tuesday, July 06, 2004

Oh, one more thing...I think the story for updating an existing API is actually better with this proposed extension.  If you change a method today, and it now throws new exceptions, you are relying on your consumers to read your updated docs (which isn't very likely) and then update their code.  If they don't read the docs and handle the new exception, they may get seemingly unexplained errors crashing the program when they drop in your new .dll, even though everything compiles fine.

At least with "Strict Exceptions" turned on, there is a compile-time warning or error raised.  Consumers still have the same choices - catch the new exception, or let it rise to the top of the stack if it happens to occur (by turning Strict Exceptions down or off for a time in the interest of getting something out the door on a deadline).  But at least you can be reasonably assured that they are aware of it.

Joe
Tuesday, July 06, 2004

The problem with virtual is that callers may have been compiled against my base class.  If the base class' definition of a method throws different exceptions than my override of that method, callers' code will not be warned about the correct exceptions.  They'll assume that only FooException can be thrown, since that's what's declared by the base class, but a subclass can throw BarException, which is not a subclass of FooException.

schmoe
Tuesday, July 06, 2004

"I'm by no means a language/compiler designer, but I'm not sure what the difficulty with virtual methods would be.  A virtual method, or an interface implementation method, would automatically inherit the throws clause of its base.  Additionally, virtual methods would be able to override the throws clause (although only in its entirety)."

That doesn't work, clearly, because the code that's compiled against the base class (or interface) doesn't ever have any contact with the derived class. It doesn't know the derived class exists (in fact, it might not yet exist when the client code is written). Since this is support to be a compile-time thing and not a runtime thing, it's impossible to allow.

Therefore, a virtual method cannot extend the throws clause.

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, July 06, 2004

Ahh, of course, virtual methods can't change the signature, that makes sense, sorry.  I still think in general it's a good idea though. 

There is one way to change a base's method signature though -- method hiding ('new' in C#).  Clearly it's very different than overriding the method and doesn't present the same kind of flexibility. 

But since we're talking about an add-on that is completely optional, I wonder what kind of viability it would have given the restriction that you couldn't change the throws clause of a base method? 

If nothing else, you could create a custom exception type and declare it in the throws clause of your base method...then any virtual methods would just have to wrap their exceptions appropriately.  Of course this isn't ideal either, but it seems workable.

Joe
Tuesday, July 06, 2004

That smells like nothing different than saying "throws(Exception)", if you ask me.

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, July 06, 2004

"If nothing else, you could create a custom exception type and declare it in the throws clause of your base method...then any virtual methods would just have to wrap their exceptions appropriately.  Of course this isn't ideal either, but it seems workable."

Yep, Java does this (InvocationTargetException, SAXException, etc.), as do several projects I've worked on.  Generally not a bad idea - the idea here is that this exception-wrapping becomes part of the interface contract.  The (minor) problem here is that the interface contract is basically dictating that the callers shouldn't care about which exceptions get thrown.  Of course, if they do care, they can catch the wrapped exception, unwrap it, and throw it to themselves - I've never seen this actually be useful, but it's possible.  And at least it allows for some documentation.  :)

(Of course, in Java, the method in question could still throw a non-wrapped unchecked exception.  Convention holds that even unchecked exceptions should be wrapped in this case, but this isn't enforced.)

schmoe
Tuesday, July 06, 2004

I spoke recently with someone who looks closely at pre-release versions of Java. Apparently the situation is that checked exceptions weren't even a serious issue, until someone pushed hard for it, and in testing it helped the team find some compiler bugs. So on the strength of one test case, it was thrown in at the last minute.

Interpret that how you wish, you could probably find very old versions and check if you're inclined.

The problem with checked exceptions for me is people don't talk about handling errors. They talk about how to handle the exceptions machinery. If it really was a virtually untested feature, I don't think it should've been in. (Many things could've found bugs in prerelease code.)

In practice, it's not clear to me that the sky will fall as a result of C#/Ruby/Python/lisp code, where exceptions are unchecked. In real Java code you will find "catch (Exception e) {e.printStackTrace();}".

Also, how else do you write many kinds of reusable code, except by generalizing methods to throw Exception, instead of some specific exception? Like Java Generic Library's old map(). (Keeping in mind the JGL hadn't been obsoleted by Collections, like the PR said.)

Tayssir John Gabbour
Tuesday, July 06, 2004

Brad, it is a step above "throws Exception" in that by saying "throws MyExceptionType" you need to explicitly wrap any exception you intend to throw.  This is different because it prevents unexpected exceptions from lazily propagating up the stack.  You might also define multiple exception types to categorize the exceptions that various implementations may throw in the context of the common operation being performed.

Of course, there is nothing stopping anyone from writing a big try{...} catch(Exception ex) {throw new MyExType(ex);} clause around their subclass's virtual method and wrapping any-and-all exceptions, but that would just be bad style, inappropriate use of the tools, and a violation of the inheritance contract.

Joe
Tuesday, July 06, 2004

schmoe -- there's an example in the .NET world of that too.  Web Service calls always wrap any exceptions thrown on the server inside a SoapException for delivery to the client.

I don't really think it means that the contract is saying you shouldn't care which exception gets thrown though.  What the contract says is that you should do this:

try
{
    ...
}
catch(MyExType ex)
{
    if(ex.InnerException is MySpecificExType)
    { ... }
    else if(ex.InnerException is MyOtherExType)
    { ... }
}

Of course that doesn't work on SoapExceptions because the contents are just plain text rather than wrapped Exception objects, but that's a specific case dealing with passing exceptions over a platform-independent remoting boundary :)

Joe
Tuesday, July 06, 2004

["Is there any documention or method to know what normal exceptions may result from an operation?"

., this is one of my biggest complaints about .NET's approach to exceptions - the libraries I've seen are poorly-documented with respect to exceptions.  I can deal with the argument that exception-handling shouldn't be compiler-enforced (though I prefer Java's approach), but when the exceptions for the stock .NET library classes aren't documented at all, I think the argument falls apart.]

The documentation is very poor, and the class designers have in many cases not followed their own guidelines. For example, Sysytem.Web.Mail, which is basically wrapping CDO, has no documented exceptions. If you use Roeder's .NET reflector (well worth having), classes in the namespace are quite clearly throwing HttpExceptions. As an example of extremely poor design, look at the MailAttachment constructor. It includes a call to a private method:

private void VerifyFile()
{
      try
      {
            File.Open(this._filename, FileMode.Open, FileAccess.Read, FileShare.Read).Close();
      }
      catch
      {
            throw new HttpException(HttpRuntime.FormatResourceString("Bad_attachment", this._filename));
      }
}

This is absolutely the pits in coding design:
1. All exceptions are caught. Any class designer who thinks they know how to handle and recover from ALL exceptions,
including non-CLS complient exceptions (which is what this catch block does) is living in cloud-cuckoo land.
2. Any original exception raised by File.Open which provides information on why the call didn't succeed is lost. It should be either wrapped or rethrown so the class library user can decide how or if they want to recover from this exception.
3. The thrown HttpException exception is not even documented.

el
Wednesday, July 07, 2004

This was also one of my main beefs with my (admittedly shallow) .NET experience. I want to know what exceptions I can expect without having to resort to a decompiler.

Just me (Sir to you)
Wednesday, July 07, 2004

Wow, I hadn't noticed that about .NET, that all exceptions are unchecked.  That's absolute crap, reducing C# to VB with squigglies.

Oh well... another language for the dustbin of mediocrity.

Bob
Wednesday, July 07, 2004

Look, the Java trolls are getting agitated. Microsoft must be doing something right!

Chris Nahr
Thursday, July 08, 2004

*  Recent Topics

*  Fog Creek Home