-
Notifications
You must be signed in to change notification settings - Fork 6
Our mission
The goal of this tutorial is to learn how to use the Checker Framework, and specifically the Nullness Checker on a real-life project.
Ideally, once we’re done we can be sure that our code is safe from
NullPointerException
s.
The Nullness Checker’s promise is that once it
issues no warnings for a given program, then running that program will never throw a null pointer exception.
To keep that promise it needs a little help from us, the programmer. We need to
make some of our knowledge of nullness in the program explicit by annotating
types as either
@Nullable
(possibly null
) or
@NonNull
(never null
). So, the signature of makeSlug
from the introduction is really
public @NonNull String makeSlug(@NonNull String s)
and the signature of getTitle
, if it might return null
, is properly
public @Nullable String getTitle()
In a sense, these annotations are like an additional type system placed on top
of the Java one, making it more precise. The idea being that something that has
a type that’s includes the null
value (the result of getTitle
, @Nullable String
) is not compatible with a type that cannot hold a null
value (the
argument of makeSlug
, @NonNull String
). We can think of these nullness
annotations as constituting a ‘pluggable type system’.
It would be pretty tedious if we had to annotate every reference in our code as
either @Nullable
or @NonNull
. As a convenience, the Nullness Checker makes a
reasonable default assumption about references: every unannotated reference is
assumed to be @NonNull
.
Just to make sure we’re on the same page, a quick word on why assuming
@NonNull
by default makes sense: let's talk about null-safe programming
practice.
The first rule of null-safe programming is don’t use null
. Others have
explained this
well. The
banal truth is if you don’t use null
you won’t get NullPointerException
s. A
number of strategies exist to avoid using null
, such as using null-objects
instead, preferring immutable value objects whose fields are never null
,
returning empty collections and Optional
instead of null
, and failing fast
when receiving null
arguments.
So, if modern programming practice shuns null
it follows that all references
are not null by default: something that's nullable is exceptional – it is the
marked case and deserves a special marking. When using the Nullness Checker, you
can think of all type uses. The Nullness Checker assumes that every type use is
non-null. In other words, it carries an implicit @NonNull
annotation.
Admittedly, this advice isn’t entirely practical: especially when we deal with
legacy code, we'll find null
to be used awfully expansively.
On the other hand, everything that may be null
, is what deserves special
marking. References that are possibly null need to stand out. There we will need
to add the @Nullable
annotation.
Mention that non-null default is modern practice, but it can be difficult to apply modern practice to sloppy, null-everywhere legacy code.
todo
Enough theory, let’s get started.
So, our mission is to learn how to use the Nullness Checker, and we’re going to learn this by integrating it into a real-world (work with me) project.
We’re going to use the Spring Framework’s Pet Clinic sample web app.
The Pet Clinic is perhaps not the most appealing project, but it does have some characteristics that make it a good candidate.
- The Pet Clinic has some typical enterprisey bits to it which many programmers must (for better or worse) work with every day.
- The Pet Clinic is a typical legacy software project. There is bound to be some shoddy code in there and that’s for the Checker Framework to chew into.
Ok, on to checking out the code and setting up the project on the next page.