Java8 Optional anti-patterns

July 10, 2017
java8 optional antipattern

What it the Optional?

Optional container was introduced with Java8. Its main purpose is to handle cases when return value might be missing. However, over the time, developers started to use it not as it was intended. Let’s talk briefly about the what it actually is and then move onto some anti-patterns.

How to use it?

Originally created for stream operations, to handle cases when value might be missing (and some default one could be provided).

According to documentation, Optional should be used as return type. And that’s all. Neat solution, for handling data which might be not present.

Anti-pattern #1 - Optional type in object field.

Optionals cannot be used in such case as they are not serializable (more about that later). Let’s describe the problem:

We have an class which one of the fields might be not present. Perfectly valid use case.

public class Car {
    private List<Wheel> wheels;
    private Optional<Engine> engine;

    // getter and setter
}

Unfortunately due to previously mentioned Optional limitations our model class needs to be rewritten. Luckily, this situations could be solved by getter method returning Optional type from nullable field, like in code below:

public class Car {
    private List<Wheel> wheels;
    private Engine engine;

    public Optional<Engine> getEngine() {
        return Optional.ofNullable(engine);
    }
}

Anti-pattern #2 - Collection of Optionals

Different use case, which also could be found is, Optional type in collections like:

    private List<Optional<Wheel>> wheels;

It is simply a code smell. There is no need to have List of Optional’s which might or might not have a value. We can have smaller or even empty List of concrete objects instead.

Anti-pattern #3 - Optional as method argument.

Optional is a value-based class, thus does not have any public constructors. We can create new instance using static methods:

Optional.empty()
Optional.of(T value)
Optional.ofNullable(T value)

using Optional as arguments, would make our method look like:

Long calculate(List<Optional<Long>> data)

now let’s use this method:

List<Long> data = Arrays.asList(1L, 2L);
calculate(Optional.of(data));

Optional wraps objects with another level of abstraction, which in that case is simply a extra boilerplate code. On the other hand we have cleaner solution without Optional.

List<Long> data = Arrays.asList(1L, 2L);
calculate(data);

Anti-pattern #4 - trying to serialize Optionals

Optionals were not designed to be serialized. Object serialization depends on object identity. If we take a look at the JavaDocs, we can see that Optional was designed to be value-object:

This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.

Reason for why Optional became a value-based class at the first place is well described by the Stuart Mark on the Devoxx conference (https://www.youtube.com/watch?v=fBYhtvY19xA). Stuart Mark states that Optional will become serializable as Value type in future thanks to project Valhalla. In case you really, really need to serialize Optional, you have to consider using different libraries, like Guava Optional or Vivr Optional.

Summary

In the short article I tried to describe use cases of Optional anti-patterns with solutions how to make it right. Like always those rules are not written in stone, but might be useful to at least be familiar with them.

comments powered by Disqus