Better Null-Handling With Java Optionals
Until Java 8, there was no better way to handle
null references than checking your variables at every turn.
The new class
java.util.Optional<T> changed that significantly.
But first, some history and comparison with other languages.
Table of Contents
A Billion-Dollar Mistake
The inventor of the null reference apologized in 2009 for its creation:
I call it my billion-dollar mistake. It was the invention of the null reference in 1965.
At that time, I was designing the first comprehensive type system for references in an object-oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement.
This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
– Sir Charles Antony Richard Hoare at QCon London in 2009
Programming languages are dealing very differently with the mistake.
Many of them have a proper way to handle
null, directly integrated into the language itself.
How do other languages handle null?
In Swift, the
null reference is called
The language has excellent support for optional types and
With its help, we can safely call properties, and even chain them fluently together:
In combination with the null coalescing operator, we can make it even better and don’t have to deal with an optional type:
This article is about Java, so let us look at an example from another JVM language.
Clojure also prefers
nil for the
But instead of providing a safe navigation operator to work around the problem, Clojure decided to try to get rid of the
nil is kind of Java’s
null, it’s just another value type.
Only one instance of
nil exists, and it tests to
It’s still possible to encounter a
NullPointerException, but it’s way harder if you use Clojure the way it’s intended.
nil being a value representing nothingness instead of being nothing, it’s way easier to handle and incorporate into your code.
Being a Smalltalk-inspired language, Objective-C doesn’t call methods or fields, it sends messages to objects.
And sending a message to
nil will not raise an exception, the message will be discarded silently.
This can be great, you don’t have to
But it’s also bad because you might not realize that a message wasn’t answered.
With the release of Java 8, a straightforward way of handling
null references was provided in the form of a new class:
It’s a thin wrapper around other objects, so you can interact fluently with the
Optional instead of a
null reference directly if the contained object is not present.
Optionals are created in different ways:
Every use-case has its own reason for existence:
Case 1: We know/need a value
Even though Optionals are a great way to deal with
NullPointerExceptions, what if we actually need one?
Just because the API design uses Optionals, there might be a need to throw an exception in specific cases.
Optional#of(...) is for, which doesn’t accept
Case 2: We’re not sure
The most used form of creation. No null check is performed.
Case 3: We’re sure there’s no value
Optional.empty() returns a
static final field.
No need to create a new Optional every time it’s empty, and we know about it for sure.
Checking for values
The easiest way to check is
To not check against
false all the time, Java 11 introduced
Get the actual value
Only checking if a value is present or not is not much of a help, we want to use it! The simplest way is
Congratulations, we’ve just thrown an exception!
Even though it wasn’t a
NullPointerException, there must be a better way than checking
isPresent() every time we use Optionals.
A Better Way
In real code, we don’t just want to get the inner value. Instead, we want to work with it. Thanks to lambda expressions, we can do this easily and lazily.
Before we got Optionals, the usual way to handle values was something like this:
With Optionals, we can compact this code into an easily-readable single-method call chain:
Java doesn’t support a null-coalescing operator like other languages:
With Optionals, we can use the same pattern by using
Optional#orElse(...) without any additional actions in between:
Actions and Alternatives
The methods of
java.util.Optional can roughly be separated into two groups: actions and alternatives.
map(Function<? super T,? extends U> mapper)
If a value is present, the mapper function will be applied, and a new Optional with the result will be returned. Otherwise, an empty Optional will be returned.
flatMap(Function<? super T,Optional<U>> mapper)
map(...), but can handle Optionals itself.
filter(Predicate<? super T> predicate)
If a value is present and matches the predicate, the Optional is returned. Otherwise, an empty Optional will be returned.
ifPresent(Consumer<? super T> consumer)
The consumer will only be invoked if a value is present.
Methods providing an alternative in the case of no value starting with
If no value is present,
orElseGet(Supplier<? extends T> other)
A lazy way to get an alternative value if none are present in the Optional.
orElseThrow(Supplier<? extends X> exceptionSupplier)
If no value is present, invoke a supplier for an exception, e.g.,
Primitive Data Types
java.util.Optional<T> being a generic type, how should we handle primitive data types like
Of course, primitives can never be
null, but that’s not necessarily a good thing.
Being able to represent nothingness, compared to not-initialized/default value, can be an advantage.
One way to deal with primitives is not using them and relying on their corresponding object wrapper class, e.g.,
int -> Integer. But this will only produce overhead from auto-boxing/unboxing.
Until Project Valhalla, with its improved value type handling, is available to us, we can use specialized Optional classes to use the power of Optionals with primitive data types:
There are still some primitive data types missing, but it’s a start. Also, they lack all action methods, except
Optionals are just Java objects like any other object, so they might be
If we design an API and decide to use Optionals, we must not return
null for an Optional, always use
This has to be enforced by convention, the compiler can’t help us out, except with additional tooling and
Other standard Java features that we shouldn’t use directly with Optionals are about equality:
hashCode() are first about comparing the Optional, and only second about the value.
The documentation warns us that the results might be unpredictable.
Optionals are a great way to handle
null references in a more concise way.
We can replace many
if-else constructs and even incorporate lambda expressions.
Instead of using
null, we should design our APIs with Optionals, if possible and applicable.
Even primitives are usable to a certain extent, so there are no more excuses for using Optionals in our next project.