Wednesday 7 March 2012

C# vs Java: Exceptions

The Web is full of C# vs Java comparisons, (heated) debates, swears, lies, love, hate... so why shouldn't I add my 2 cents!?

  • I think it's obvious that one of the first differences (annoyance I would call it) that one finds between Java and C# are Checked Exceptions. It's rather odd that such a conservative language like Java decided to include a so experimental feature (suffice to say that no other popular language had or has included it, much less after the disconform and frustration that they seem to cause in many programmers).

    The many people that dislike checked exceptions (I'm not going to discuss if they have some benefits, the net is full of heated discussions about it, but I'm one more of those many people that coming from any of those (many) languages that lack of them finds them disturbing and can't get accustomed to them) try to avoid them to a certain extent by deriving all their custom exceptions from RuntimeException instead of Exception. Anyway, all the Java base library is full of them, so for most common operations (IO, Threading) you'll end up having to deal with them (out of tiredness in many cases it just means swallowing them).

    Due to this general frustration, the designers of more friendly and beautiful languages like Groovy or Scala decided not to care about Checked exceptions. You can invoke a method that throw checked exceptions and the compiler won't force you to wrap it into a try-catch or add a throws declaration to the method signature. Good, but, how do they achieve this?

    Well, thought the throws clausule exists at the bytecode level, the runtime does not give a damn about it when interpreting or JIT compiling those bytecodes (and neither does the bytecode verifier). This means that it's the Java compiler, not the runtime, who's imposing you the checked exceptions restrictions. The Groovy and Scala compilers just don't do that... This nice explanation:

    While the JVM bytecode includes checked exception specifications on methods, the bytecode verifier that runs before any bytecode is executed specifically doesn't check that the methods actually conform to exception specifications. You could write a program that took existing JVM bytecode and removed all exception specifications, and the resulting bytecode would be perfectly valid and run identically to the original (barring reflection).

    can be found here
  • Another interesting difference between Java and C# with respect to exceptions has to do with Exception rethrowing (and this time I would say it's C# who has a "bad" (let's say prone to confusion) behaviour.

    In C#, when we are inside a catch block and we want to rethrow the exception, we have two options: throw ex; and throw;. The former rethrows the exception, but previously removes the stack trace, the latter just rethrows the exception with its whole stack trace.
    In Java we only have throw ex;, that just rethrows the exception with its whole stack trace (same as C#'s throw;). If we wanted to clean the Stack trace we would have to create a new exception and throw it. I think I've never needed to clean the Stack trace before throwing a exception (in those cases where I wanted to hide internals I would be throwing a new custom exception, not a cleaned up version of the original exception, so for me that option given by C# only adds confusion. You can read more here and here.

  • Catching multiple exceptions. Java 7 added Multicatch. You can catch different types of exceptions in the same catch block:

    catch (FirstException | SecondException ex) {
         logger.error(ex);
        throw ex;
    }
    
    So far, in C# you can only catch one type of exception in each catch block, so you would have to use something a bit more verbose like the technique described here
    catch (Exception ex)            
    {                
        if (ex is FormatException ||
            ex is OverflowException)
        {
            WebId = Guid.Empty;
            return;
        }
        else
        {
            throw;
        }
    }
    

  • Automatic Resource Management Java 7 has finally added ARM, that works basically the same as C#'s using blocks (available in C# since the initial version). In Java the objects eligible for ARM have to implement the new Autoclosable interface, in C# they have to implement IDisposable. In both cases what the compiler does under the covers is wrap the code in a try, and add a finally where the calls to close or Dispose are done. A good way to avoid boilerplate code. You can read more here

No comments:

Post a Comment