Here comes part 2 of the "Java vs C#" series. I still want to remind you to read
part 0 and
part 1 because they contain my motivation and important side notes but if you do not feel like reading them just remember that I only compare the C# and the Java language and claim that C# is better but do not compare the Java and .NET platforms and do not claim anything about the platforms as a whole (at least not in these articles). Read on to find out why Java's approach of letting methods be virtual by default is wrong.
In Java methods are virtual by default and matching methods override base class methods by default. This can turn into a problem with versioning. Imagine that you have a class A and a class B that inherits from A and has a method void foo(). At some point a developer who has no idea about class B adds a method void foo() to class A. Then your class B overrides the method in A. This happens silently with no compiler error and possibly no runtime error but results in wrong behavior due to unintended polymorphism. This is really bad for maintainability and we already agreed in part 0 that maintainability is the most important goal of C# and Java.
On the other hand what if we wanted to have a method foo() in class A and override it in class B but someone changes the method name in A to bar()? Then the code would still compile despite the fact that the bar method is not overridden as intended. This makes refactoring and versioning harder.
C# solves these problems by forcing virtual base methods to be marked with the virtual keyword and overriding method to be marked with the override keyword. This way no unintended behavior can be compiled and run without errors.
Java designers have acknowledged and partially solved the second problem by introducing the @Override annotation in Java 6. However it is optional and as to my knowledge does not solve the first problem. Probably there are options in the compiler to make the @Override annotation mandatory for methods that override something but it is not on by default. Using annotation for something that should be a keyword hurts language consistency. If @Override is an annotation why is final a keyword? However annotation is probably the best decision in this legacy situation.
Adding insult to injury virtual methods hurt performance (due to virtual dispatch) and developers just forget to seal them.
More in depth commentary by Anders (Hallowed be His name!) himself can be found in this
interview.
In the C# language there are more cases of design targeted at maintainability and versioning. For example the overload resolution does not choose the best overload match in general but the best overload in the most derived class that has any match. This may not seem logical at first because developers expect the compiler to choose the best overload (as in Java) but prevents silent changes in program behavior when a better match is added to the base class. This is a good tradeoff because the developer surely will notice that his method does not behave right when he has just written it even if he does not know the language rules that govern this behavior but in practice he cannot notice the change in the behavior when he or someone else adds a new method to a base class in a later version of the code.
Stay tuned for part 3 which will deal with exceptions.