The Java ecosystem’s obsession with NonNull annotations
Image of wooden dummy with worried stressed thoughts image via Shutterstock
In this article, Java champion Lukas Eder weighs in on the Java ecosystem’s obsession with NonNull annotations and explains why using type annotations is a very strategic decision.
I’m not well known for my love of annotations. While I do recognise that they can serve a very limited purpose in some areas (e.g. hinting stuff to the compiler or extending the language where we don’t want new keywords), I certainly don’t think they were ever meant to be used for API design.
“unfortunately” (but this is a matter of taste), Java 8 introduced type annotations. An entirely new extension to the annotation type system, which allows you to do things like:
@Positive int positive = 1;
Thus far, I’ve seen such common type restriction features only in the Ada or PL/SQL languages in a much more rigid way, but others may have similar features.
The nice thing about Java 8’s implementation is the fact that the meaning of the type of the above local variable (@Positive int) is unknown to the Java compiler (and to the runtime), until you write and activate a specific compiler plugin to enforce your custom meaning.The easiest way to do that is by using the checker framework (and yes, we’re guilty at jOOQ. We have our own checker for SQL dialect validation). You can implement any semantics, for instance:
// This compiles because @Positive int is a subtype of int int number = positive; // Doesn't compile, because number might be negative @Positive int positive2 = number; // Doesn't compile, because -1 is negative @Positive int positive3 = -1;
As you can see, using type annotations is a very strategic decision. Either you want to create hundreds of types in this parallel universe as in this example:
The only thing crazier than annotations are type annotations. On arrays. Who thinks this is valid Java code? pic.twitter.com/M9fSRRerAD
— Lukas Eder (@lukaseder) March 20, 2016
Or, in my opinion, you better leave this set of features alone, because probably: YAGNI
Unfortunately, and to the disappointment of Mike Ernst, the author of the checker framework (whom I’ve talked to about this some years ago), most people abuse this new JSR-308 feature for boring and simple null checking. For instance, just recently, there had been a feature request on the popular Javaslang library to add support for such annotations that help users and IDEs guarantee that Javaslang API methods return non-null results.
Please no. Don’t use this atomic bomb for boring null checks
Let me make this very clear:
Type annotations are the wrong tool to enforce nullability
– Lukas Eder, timeless
You may quote me on that. The only exception to the above is if you strategicallyembrace JSR-308 type annotations in every possible way and start adding annotations for all sorts of type restrictions, including the
@Positive example that I’ve given, then yes, adding nullability annotations won’t hurt you much anymore, as your types will take 50 lines of code to declare and reference anyway. But frankly, this is an extremely niche approach to type systems that only few general purpose programs, let alone publicly available APIs can profit from. If in doubt, don’t use type annotations.
One important reason why a library like Javaslang shouldn’t add such annotations is the fact that in a library like Javaslang, you can be very sure that you will hardly ever encounter
null, because references in Javaslang are mostly one of three things:
- A collection, which is never
null(it is in fact a collection of cardinality
- A non-
nullreference (because in the presence of
Option, all references can be expected to be non-
Of course, these rules aren’t valid for every API. There are some low quality APIs out there that return “unexpected”
null values, or leak “internal”
null values (and historically, some of the JDK APIs, unfortunately, are part of these “low quality APIs”). But Javaslang is not one of them, and the APIs you are designing also shouldn’t be one of them.
So, let go of your
null is not a problem in well-designed software. You can spare yourself the work of adding a
@NonNull annotation on 99% of all of your types just to shut up your IDE, in case you turned on those warnings. Focus on writing high-quality software rather than bikeshedding