In Java codebases it is very typical to come across code similar to the following:
interface PlaneProvider {
Plane getPlane();
}
The contract for PlaneProvider.getPlane()
indicates that a Plane
will be returned. Typically a client using this interface would use it writing something like this:
Plane plane = planeProvider.getPlane();
if (plane != null) {
plane.fly();
}
Notice that if the client doesn’t perform the null check and a null
is returned by getPlane()
we would get a NullPointerException
at runtime when we call plane.fly()
.
The Groovy ?. operator
Groovy provides the ?.
operator in an attempt to provide a cleaner way to write the client code:
Plane plane = planeProvider.getPlane()
plane?.fly()
If plane
is null
the call to fly()
will not be performed.
Enter Java 8 Optional
Using the new Optional<T>
type in Java 8 we could rewrite the PlaneProvider
interface above like this:
import java.util.Optional;
interface PlaneProvider {}
Optional<Plane> getPlane();
}
Just by reading the signature of PlaneProvider.getPlane()
we can immediately tell that a plane may or may not be returned. The concrete implementation of PlaneProvider
would use Optional.of(somePlane)
to return a value or Optional.empty()
to indicate what was previously indicated with a null
value:
class RyanairPlaneProvider implements PlaneProvider {
Optional<Plane> getPlane() {
havePlane ? Optional.of(plane) : Optional.empty();
}
}
In the client we can then use Optional.isPresent()
and Optional.get()
to check for the presence of a value and retrieve it if we want, leading to more readable code:
Optional<Plane> plane = planeProvider.getPlane();
if (plane.isPresent())
plane.get().fly();
The Optional
class provides additional methods which support a more functional style when writing code:
Optional<Plane> plane = planeProvider.getPlane();
plane.ifPresent(() -> plane.get().fly());
//or
plane.ifPresent(Plane::fly);
We can also model the case where a plane cannot be provided by the PlaneProvider
using orElse()
and using another object sparePlane
as fallback:
Optional<Plane> plane = planeProvider.getPlane();
plane.orElse(sparePlane).fly();
And in the case where we don’t have any spare planes we can write:
Optional<Plane> plane = planeProvider.getPlane();
plane.orElseThrow(IllegalStateException::new).fly();
Not on Java 8?
If for some reason you’re on a version of Java prior to Java 8 you can still use Optional
by using the Guava library.