Optional, Guava and Java 8

Edouard Kaiser
6 min readMar 7, 2018

--

The Guava’s aficionado have been dealing with the concept of Optional for a while (since Guava 10 actually, it’s been 5 years). As usual, I’m not going to talk about how to use Optional, I’m more interested in the way Google implemented the concept, the Java 8 implementation and the differences between the two of them.

It took two years and a half to feel this gap in the JDK, and the choices made by the JDK expert groups are interesting in terms of implementation but also naming convention.

But let’s start with the Guava implementation.

Guava

Optional is an abstract class, implementing Serializable. It might look like a small detail, but it’s not, I’ll come back to this point while talking about the Java 8 implementation.

public abstract class Optional<T> implements Serializable { 
public abstract boolean isPresent();
public abstract T get();
public abstract T or(T defaultValue);
....
}

The Guava team decided to split the concept of the Optional, being empty or not, by creating two different implementation: Absent and Present.

These classes are not accessible directly, the default constructor of Optional has been restricted to the package, the underlying implementations don’t offer any public constructors, they just inherit the one from Optional which is not accessible, and to enforce this, the classes Absent and Present are only visible at the package level:

final class Absent extends Optional<Object> { .... } 
final class Present<T> extends Optional<T> { .... }

Optional is a very dummy class at this point, all the instance methods are abstract (isPresent, get, or…), the only implementation are the one to build an Optional:

public static <T> Optional<T> of(T value) { 
return new Present<T>(checkNotNull(value));
}
public static <T> Optional<T> fromNullable(@Nullable T value) { return (value == null) ? Optional<T>.absent() : new Present<T>(value);
}

The of(T reference) method will always return a Present object, if the parameter is null, a NullPointerException will be thrown.

The fromNullable will return an Absent object if the parameter is null, otherwise Present. Nothing fancy so far, the interesting part relies in this separation between Absent and Present.

By definition, Optional is an immutable object, considering this, there is no reason for the method isPresent() to check every time if the value in the Optional is null or not, because between two calls it can’t change.

So, once an Absent object has been built, it will always represent an absence of value, the implementation of the methods get() or isPresent() becomes very straightforward:

@Override 
public boolean isPresent() {
return false;
}
@Override
public Object get() {
throw new IllegalStateException("value is absent");
}

We might think it’s a matter of optimisation, but today the JVM would optimise an implementation like this:

public boolean isPresent() { 
return value != null;
}

The value being final, modern JVMs can easily optimize this kind of code with method inlining and JIT compilation. So in the end, it’s more a separation of concept than a real optimisation.

There is only one instance of Absence object per JVM (static field of Absence instantiated when the class is loaded). Optional always use this instance when an Optional is created from a null reference. That means it doesn’t matter if you represent an Optional<String> or Optional<Integer>, if your Optional is empty, both variable will point to the same instance.

The implementation of Present follows the same pattern, immutability offers a straightforward path, the value will never be null:

@Override 
public boolean isPresent() {
return true;
}
@Override
public T get() {
return value;
}

Like any other Java Object, Optional offers hashCode and equals method. The hashCode is interesting, the Absence object always return the same magic constant: 0x598df91c, but, for Present, the formula is a combination of this magic constant and the hashCode of the value:

@Override 
public int hashCode() {
return 0x598df91c + value.hashCode();
}

I guess, they wanted to differentiate the value itself and the concept of the value wrapped in a container by not relying on the hashCode() of the value directly.

Java 8

The Java 8 implementation is very straightforward. Contrary to Guava, there is no separation of concept. The java.util.Optional is a final class inheriting from java.lang.Object.

And just like the Guava one, the constructor is not visible, the Optional gets built through the two main static methods:

public static <T> Optional<T> of(T value) { 
return new Optional<>(value);
}
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}

Even if there is a small difference in the naming of the method to get an instance from a potential null reference, the design makes the refactoring from the Guava one, to the Java one, very easy.

We can find common methods, like get(), isPresent(). They represented the absence of value with the empty() method which returns an empty Optional obviously (named absent() in Guava).

Nothing fancy in the implementation so far:

public boolean isPresent() { 
return value != null;
}
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}

The interesting part lies in the interaction you can get from the JDK’s Optional with functional programming paradigm. Just like the Guava one, it offers the methods or(Supplier<? extends T> supplier) (it’s actually orElseGet in the JDK) and transform (map in the JDK). But the JDK goes further with filter and flatMap:

public Optional<T> filter(Predicate<? super T> predicate) public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)

Predicate and Function are new functional interfaces introduced in Java 8, but these interfaces have been available in Guava since the beginning (version 2.0 actually), I’m surprised they don’t provide filter and flatMap. Even the transform method took 8 months to be introduced in Guava after they released the Optional feature. You might say, the difference between flapMap and map is very small, it’s just about the mapper returning an Optional directly, but still it doesn’t cost anything in terms of implementation and maintenance.

Regarding the hashCode, they took a different direction. They don’t separate the concept of the value wrapped in a container and the value itself, they rely directly on the hashCode of the value:

@Override 
public int hashCode() {
return Objects.hashCode(value);
}

That means if the Optional is empty (value == null), 0 will be returned, otherwise the hashCode of the value directly.

Serializable

The main difference between the Java 8 and the Guava implementation (beside the functional programming orientation of the Java 8 with map, flatMap and filter) is the decision of the Java expert group to not make Optional serializable (contrary to the Guava one). This decision initiated intense debates other the internet. That means, if you’re using serialization and Guava Optional, the migration to the JDK’s Optional will break your serialization system with a nice java.io.NotSerializableException.

Why this decision? So far I can see two answers: one from a technical/maintenance point of view and the other one from a concept point of view (the way they think Optional should be use).

During the elaboration of the JSR-335 (Lambda expression, Stream, Optional bound…), someone came up with the name OptionalReturn to enforce the design orientation of the feature. Yes, they considered that Optional should not be any more than a support the optional-return idiom only. It wasn’t meant to be used as a field for classes. This is why Optional hasn’t been marked with Serializable in terms of design/concept point of view. However, even if you decide to use Optional, only in the context of optional-return pattern, your methods could not be used through RMI.

About the technical reason, I will let Brian Goetz (Java language architect at Oracle) give the explanation itself. If someone knows better than us, that’s probably him, here is his quote from a mailing discussion:

Making something in the JDK serializable makes a dramatic increase in our maintenance costs, because it means that the representation is frozen for all time. This constrains our ability to evolve implementations in the future, and the number of cases where we are unable to easily fix a bug or provide an enhancement, which would otherwise be simple, is enormous.

Beside of the functional programming orientation, I think Optional are all about improving readability and enforcing public interface contracts. When you’re dealing with private class fields, it’s up to you to manage what’s happening behind the scene. It’s not necessarily a bad practice to use it as class field, but a pattern like this wouldn’t be wrong neither and at least still serializable:

@Nullable private final String flightNumber; public Optional<String> getFlightNumber() { 
return Optional.ofNullable(this.flightNumber)
}

And regarding the improvement they bring, coupled to functional programming, they are a very exciting concept.

--

--

Edouard Kaiser

Senior Engineering Manager @Atlassian, technology lover, Java engineer originally, distributed systems aficionado and team builder.