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.