Rethinking null when Java has value types.
Null in Java
Was null really the billion dollar mistake?
Well, thats an interesting question, but what is certain is that null is widely
used in Java, and frequently a very useful concept.
But, just because it is useful doesn't mean that there isn't something better.
Optional everywhere!
There are those that would point you to adopt the following strategy -
avoid using null everywhere, and use Optional if you need to express
the possible absence of something.
This has a certain attraction, but I firmly believe the Java is not yet ready for this (in Java SE 8)
as per my previous post on Optional.
Specifically, all the additional object boxes that Optional creates will have an effect of memory
and garbage collection unless you are very lucky.
In addition, its relatively verbose in syntax terms.
Nullable annotations
A second approach to handling null is to use the @Nullable and @NonNull annotations.
Except that it is only fair to point out that from an engineering perspective, annotations are a mess.
Firstly, there is no formal specification for nullable annotations. As such, there are multiple competing implementations - one from FindBugs, one from JSR-305, one from IntelliJ, one from Eclipse, one from Lombok and no doubt others. And the semantics of these can differ in subtle ways.
Secondly, they don't actually do anything:
@javax.annotation.ParametersAreNonnullByDefault
public class Foo {
public StringBuilder format(String foo) {
return new StringBuilder().append(foo);
}
public StringBuilder formatAllowNull(@Nullable String foo) {
return new StringBuilder().append(foo);
}
}
Here, I've use @javax.annotation.ParametersAreNonnullByDefault to declare that all parameters
to methods in the class are non-null unless otherwise annotated, that is they only accept non-null values.
Except that there is nothing to enforce this.
If you run an additional static checker, like FindBugs or your IDE, then the annotation can warn you if
you call the "format" method with a null.
But there is nothing in the language or compiler that really enforces it.
What I want is something in the compiler or JVM that prevents the method from being called with null, or at least throws an exception if it is. And that cannot be switched off or ignored!
The nice part about the annotations is that they can flip the default approach to null in Java,
making non-null the default and nullability the unusual special case.
Null with value types in JDK 10
The ongoing work on value types (target JDK 10 !!) may provide
a new avenue to explore.
Thats because Optional may be changed in JDK 10 to be a value type.
What this might mean is that an Optional property of a Java-Bean would no longer be an object
in its own right, instead it would be a value, with its data embedded in the parent bean.
This would take away the memory/gc reasons preventing use in beans, but there is still the syntactic overhead.
Now for the hand-waving part.
What if we added a new psuedo keyword "nonnull" that could be used as a modifier to a class.
It would be similar to @javax.annotation.ParametersAreNonnullByDefault but effectively
make all parameters, return types and variables in the class non-null unless modified.
Then, what if we introduce the use of "?" as a type suffix to indicate nullability.
This is a common syntax, adopted by Fantom, Ceylon and Kotlin, and very easy to understand.
Basically, wherever you see String you know the variable is non-null
and wherever you see String? you know that it might be "null".
Then, what if we took advantage of the low overhead value type nature of Optional
(or perhaps something similar but different) and used it to represent data of type String?.
Given this, a "nonnull" class would never be able to see a null at all.
This step is the most controversial, and perhaps not necessary - it may be possible
for the compiler and JVM to track String and String?
without the Optional box.
But the box might come in useful when dealing with older code not written with the "nonnull" keyword.
Specifically, it might be possible to teach the JVM to be able to auto box and un-box
the Optional wrapper.
It might even be possible to release a standard annotation jar that projects could use on codebases
that need to remain compatible with JDK 8 or 9, where the annotation could be interpreted by a JDK 10 JVM
to provide similar nullability information.
public nonnull class Foo {
public StringBuilder format(String foo) {
return new StringBuilder().append(foo);
}
public StringBuilder formatAllowNull(String? foo) {
return new StringBuilder().append(foo);
}
}
The example above has been rewritten from annotations to the new style.
It is clearly a shorter syntax, but it would also benefit from proper integration into the compiler and JVM
resulting in it being very difficult to call "format" with a null value.
Behind the scenes, the second method would be compiled in bytecode to take a parameter of
Optional<String>, not String.
The hard part is the question of what methods can be called on an instance of String?.
The simple encoding suggests that you can only call the methods of Optional<String>,
but this might be a little surprising given the syntax. The alternative is to retain the use
of null, but with an enforced check before use.
The even harder part is what to do with things like Map.get(key) where null
currently has two meanings - not found and found null.
This concept also appears to dovetail well into the value type work.
This is because value types are likely to behave like primitives, and thus cannot be null
(the value type has to be boxed to be able to be null).
By providing proper support for null in the language and JVM, this aspect of value types,
which might otherwise be a negative, becomes properly handled.
It should also be pointed out that one of the difficulties that current tools have in this space is that the JDK itself is not annotated in any way to indicate whether null is or is not allowed as an input or return type. Using this approach, the JDK would be updated with nullability, solving that big hurdle.
(As a side note, I wrote up some related ideas in 2007 - null-safe types and null-safe invocation.)
Just a reminder. This was a hand-wavy thought experiment, not a detailed proposal. But since I've not seen it before, I thought it was worth writing.
Just be careful not to overuse Optional in the meantime.
Its not what it was intended for, despite what the loud (and occasionally zealot-like) fans of
using it everywhere would have you believe.
Summary
A quick walk through null in Java, concluding with some hand-waving of a possible
approach to swapping the default for types wrt null.
Feel free to comment on the concept or the detail!