Optional Anti-Patterns
Optionals have proven so useful that some devs have taken them out of their native habitat. Here are anti-patterns and code smells to avoid.
Join the DZone community and get the full member experience.
Join For FreeThe Optional container was introduced with Java 8. Its main purpose is to handle cases when a return value might be missing. However, over time, developers started to use it not as it was intended. Let’s talk briefly about what it actually is and then move onto some anti-patterns.
How to Use It
Originally created for Stream operations to handle cases when values might be missing (and some default one could be provided). According to the documentation, Optional should be used as a return type. And that’s all. It's a neat solution for handling data that might be not present.
Anti-Pattern #1: Optional Types in Object Fields
Optionals cannot be used here because as they are not serializable (more about that later). Let’s describe the problem:
We have a class where 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 the previously mentioned Optional limitations, our model class needs to be rewritten. Luckily, this situation could be solved by a getter method returning an Optional type from a nullable field, like in the code below:
public class Car {
private List<Wheel> wheels;
private Engine engine;
public Optional<Engine> getEngine() {
return Optional.ofNullable(engine);
}
}
Anti-Pattern #2: Collections of Optionals
Another use case you might run into is the Optional type in collections like:
private List<Optional<Wheel>> wheels;
This is simply a code smell. There is no need to have List of Optionals that might or might not have a value. We can have a smaller or even empty List of concrete objects instead.
Anti-Pattern #3: Optional as a Method Argument
Optional is a value-based class, thus it does not have any public constructors. We can create a new instance using static methods:
Optional.empty()
Optional.of(T value)
Optional.ofNullable(T value)
Using Optionals 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 extra boilerplate code. On the other hand, we have a 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-based:
"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" — from the Javadoc.
The reasoning as to why Optional became a value-based class in the first place was well-described by Stuart Mark at the Devoxx conference. Stuart Mark states that Optional will become serializable as a value type in the 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 this short article, I tried to describe use cases where Optional anti-patterns arise with solutions to make them right. Like always, those rules are not written in stone, but it might be useful to at least be familiar with them.
Published at DZone with permission of Przemyslaw Magda. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments