Bad Defaults in Java

Many misconceptions in Java and similar languages stem from the bad default behaviour. I’ll explain some of them.

final keyword

You can make a class, a method, or a variable “final”. The name of that keyword is misleading. But there’s another problem. Why aren’t fields final by default? From a programmers perspective it’s an added functionality of a field if it can be modified (multiple assignments of values, instead of just setting a value once). The runtime doesn’t really care anyway.

It would be much clearer and easier to learn if final was the default behaviour and we’d have do add a keyword to make it non-final. A class could be extendable. A method could be overwritable. And a field could be a variable instead of a value. This would be better because the use of final never needs to be justified and therefore doesn’t need any documentation. But if a class isn’t final then there must be clear information on how to extend it and how to implement all methods in extending types. If a field can change then it must be described how multiple threads can synchronize.

break in switch statements

Without break you get fall through execution. So in most cases you end up writing break for each case. Fall through is rarely needed. So why not a keyword for that? Or even use the existing (but now unused) goto keyword? You could simply go to another case.

null as default value

All fields get initialized as null. But to write robust software you do not want to handle the mess you get with null references. You want to use Optional or only have a few variables that can be null and are annotated as @Nullable. Null references should be the exception, not the norm.

C#, which is similar to Java, actually has nice solutions for this. You can define default values for your types. That’s how fields can be nonnull by default.

Methods in java.lang.Object

There are some methods in the Object class, so all objects have them. One problem is clone(), which should be in the Cloneable interface instead. This design is just broken. At least it already throws CloneNotSupportedException, so you don’t have to add this to every class you write.
Having hashCode() and equals(Object) on every object doesn’t really help either. Would it not make more sense to have an interface for this, so a HashMap could specify that the key type must extend that type? Instead of an exception you just get some random hash code and equals just returns false except for when compared to itself.
The method toString() is fine, as it doesn’t matter when a type doesn’t use it. But wait(), notify(), and notifyAll() are for mutex types and nothing else. Now other APIs have to use other names, such as await and signal.
And then there’s the worst of all: finalize()
At least it’s deprecated now. So we can hope they will remove it completely.

How to fix these problems

All of the issues can be avoided by using some software to check code quality. For Java you can use CheckStyle. And there are tools to automatically add private and final to new fields and methods. All this is not necessary with a modern language that already has proper default behaviour.

Leave a Reply

Your email address will not be published. Required fields are marked *