One of the most common exceptions in Java is the
NullPointerException – simply put this means that somewhere in your program a method is being called on a
This can be very frustrating and leads to cluttering the code with overly defensive
null checks. This frustration has lead to some languages, like JVM-compatible Kotlin, to be created to solve this issue statically by outlawing
null unless explicitly allowed in the types.
How do we reduce these
NullPointerExceptions in Java, where we don’t have language-level support like Kotlin? We can use a wrapper class introduced in Java 8, called
Very simply put,
Optional is a thin wrapper around another object. The optional instance is either present (and non-null), or empty.
However, this doesn’t give us too many benefits since it is just as safe as checking against
null. The power with
Optional is the many utility methods that is exposes to us:
You can break down the Optional methods into a few overarching categories – creating an Optional, checking if the Optional is present, “unboxing” the Optional, and interacting/transforming the Optional without unboxing it.
Creating an Optional
There are three ways to create an
Optional object –
Typically if you’re converting a value to optional, use
Optional.ofNullable. If you’re returning a known value in a method, use
Check if the Optional has a value
There are two methods that return whether the optional variable has a value,
isPresent(). They both return a boolean and are opposites of each other.
“Unboxing” the Optional
There are a few ways to “unbox” the value – that is, convert between the
Optional type and the underlying type.
The first and simplest is
get. This returns the value inside the optional if it exists, or throws an exception if it’s missing. Warning, this (intentionally) leads to runtime exceptions, so this might not be what you want!
A much safer alternative to
orElse(), which accepts a default value to return if the value is missing. This then guarantees that the unboxed value is present, and not null.
.orElseGet(() -> defaultValue)
orElseGet functions exactly the same as
orElse, but takes a function to run to get the default value instead of the value itself. This is useful to avoid running the default function unless it’s needed.
.orElseThrow(() -> new CustomException())
get() throws an exception by default if the value is missing. However, it may be useful to throw an exception of a particular type.
Interacting with the Optional without unboxing
Those unboxing methods are useful, but as soon as we unbox the value to its underlying type we lose null safety. Ideally we can keep these values wrapped in an
Optional until we get the final value.
Optional gives us a few methods to help:
.ifPresent(item -> use(item))
This method will call the lambda given with the unboxed value if it exists, or else skip the method. This is equivalent to calling
if (item.isPresent()) first, but more concise.
.map(item -> transform(item))
Map lets you transform the value to a new value. This works exactly like
.map in other languages, except it only maps if the value is present. This is chainable!
.filter(item -> conditional(item))
Filter also works the same as in other languages. If the given lambda returns true, then the filter expression returns an
Optional of the original value. If it returns false, then it will be an empty
Optional. Once again, this can be chained!
Alternative way to pass Functions
One note for all the
Optional methods that accept functions, like
filter, etc – those can also accept Method References. Method references look like double colons (
::). This can reduce some of the boilerplate and increase readability.
Putting it all together
In summary, Java’s
Optional class gives us a lot of tools to help avoid unnecessary
NullPointerExceptions in our code. We can push some of the null safety into the type system itself without needing to use a different language like Kotlin. In some cases, this can make the code more concise, too!
This post only detailed a few of the most common and useful
Optional methods. For the full list, the Java docs are a useful reference.