Here is part 3 of the Java vs. C# series which should have been called "Why C# Is Better Than Java". I remind you to read
part 0 in order to get familiar with the motivation behind the articles.
Part 1 and
part 2 are also recommended.
If I have to pick the single worst flaw in the Java language design it would be the inclusion of checked exceptions (It would be a tough fight with the primitive types though). If Java was one of these pure functional languages where functions are not allowed to have any undeclared side effects I would be the first to support checked exceptions because the truth is that exceptions are part of the function's return type and should be declared somehow in the signature. However Java is not anything like Haskell and in Java checked exceptions are actually evil. The problem is that they provoke undisciplined, lazy or incompetent developers to write even worse code while providing no additional value to good developers.
How do they provoke bad code? When a lazy developer is forced to catch an exception that he does not need to catch what he does is write an empty catch. This is far worse than forgetting to catch an exception when it needs to be caught because it is covering an error. Things get even worse when someone is forced to catch multiple exceptions. This is when a bad developer is likely to catch Exception in an empty catch and cover all exceptions not only the checked ones. This is known as Pokemon Exception Handling or as I like to call it - defeating the exceptions. To make matters worse it is hard to judge whether a method should throw checked or runtime exception even for an experienced developer. I believe frameworks like Spring and Hibernate even wrap some of Java's checked exceptions into runtime exceptions.
To be honest there is some value in checked exceptions namely they serve as documentation. While having documentation is undoubtedly useful the idea that the compiler should enforce the presence of documentation does not feel right to me. What is more I do not see any reason why possible runtime exceptions should not be listed in the documentation and why it cannot happen automatically. It seems to me that the compiler can easily write runtime exceptions in the method metadata and copy this information into calling methods when they are compiled. I wonder why compilers do not this. If anyone knows please let me know.
If documentation is helpful why do I claim that it does not help good, disciplined developers write better code? Firstly because good, disciplined developers write documentation and read documentation (at least the JavaDoc/XML comments). The good developer will handle the cases that require checked exceptions anyway but what is he going to do if he knows an exception cannot occur? He is going to wrap it in a runtime exception of course but why force him to write code that nobody needs instead of throwing a runtime exception in the first place? What about exceptions that always need to be caught? Well in my opinion they do not exist. One reason is that methods have input and output and represent a functional dependency. What if I programmatically generate all the input? In this case I can be absolutely sure that I will not get an exception. However in some cases you cannot control all of the input for example if the method calls into a system you do not control (or in the lighter case if you control it indirectly as is the case with databases). While this case is definitely more reasonable than the simple input validation case there is still a problem with it. Checked exceptions assume that the system that is supposed to catch them can recover from the exception and often this is not the case. It is a bad practice to catch exceptions from which the system cannot completely recover. In this case the exception should be rethrown through the entire stack right to the main method which effectively turns it into an annoying special case of runtime exception.
Checked exceptions have proven a burden to the language now when the designers are trying to add lambda expressions (a.k.a. closures) because they are actually methods that can throw a checked exception. The good news is that a number of people are
suggesting that
checked exceptions should be removed from the language and one day this can happen. Backward compatibility will be preserved because really there is nothing special about checked exceptions at runtime.
In an
interview on checked exceptions with Anders (Hallowed be His name!) himself. He makes a point about versioning that I do not completely agree with. He claims that checked exceptions are bad for versioning because when a method adds new features it might throw new exceptions which will break existing clients. At first it looks like a good thing but as Anders (Hallowed be His name!) points out this is not always the case because most exceptions are actually unhandled and this is how it should be. I am not sure if the tradeoff between informing the developers and not breaking the code is in favor of runtime exceptions but at best the match is even and in a good language design you do not add a feature to trade one problem for another.
Oh and if you did not know before reading this article you probably already know how C# solves the checked exception problem. It does not have them.
Well this is all for now. I cannot think of any other language design flaws in Java that are worth of an article. As I pointed out earlier it would be unfair to list features that C# has and Java lacks before Java 7 is released. I hope that you the readers learned something about language design from these articles or at least learned how to avoid misuse of certain features.