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:
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 vavr library to add support for such annotations that help users and IDEs guarantee that vavr 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 strategically embrace 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 vavr shouldn’t add such annotations is the fact that in a library like vavr , you can be very sure that you will hardly ever encounter
null, because references in vavr 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 vavr 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
And, if you haven’t had enough bikeshedding already, consider watching this entertaining talk by Stuart Marks: