Last Sunday, an unsuspecting Redditor kicked the ant hill by starting yet another conversation about
Optional - and I was thrilled to see it!
I love discussing this topic and so I immediately started writing the script on it for the next Inside Java Newscast.
That turned out to be way too long, so in the video I focus on the most common argument (overloading methods instead of using
Optional) and categorized the opinions on
Optional to give these conversations a bit more structure.
This blog post here contains the remainder of my thoughts on and replies to the Reddit thread.
A common argument against using
Optional was brought forth by Stuart Marks himself:
If you have a method parameter of type
Optionalyour callers still have to pass something, whether it's
Optional.empty(), so it clutters up the call sites.
That's true and it can be annoying. But there's a good retort to that and it was even put forward in one of the other threads:
If you want to pass a value, there's a decent chance that it's already wrapped in an
Optionalin the current scope (it turns out that stuff that's optional in a downstream method is frequently optional in the current method).
I gotta say, that's my experience as well.
Usually, optional parameters pop up because the first caller, the one for which the method was originally introduced, had an
Optional on their hands, so no wrapping is needed there.
And because most systems seem to require most data to be present, chances are good that the absent value is also frequently absent for other callers, too, so they don't need to wrap either.
What I find interesting here is how this seems to be the flip side of the overload selection I describe in the video. There, I talk about three ways to model absence:
- You can create a single method that expects some of its arguments to be
- You can hide that method and create
null-rejecting overloads that forward to it.
- You can use
Optionalfor all potentially absent parameters.
Now, let's see how those three variants behave in two different situations.
If you have a few arguments and know neither of them is
- Call the
null-accepting method, padding the absent arguments with
- Easily select the overload that matches the arguments you have.
- Call the
Optional-accepting method with some
ofcalls in there.
If, on the other hand, you got a bunch of arguments, with some of them potentially absent...
- Simply call the
- Build an
if-elsechain to select the correct overload.
- Call the
Optional-accepting method, maybe with some wrapping involved.
null-variant comes off pretty well in this comparison, but remember that is it the sneaky one where you're never sure what can be
null and why.
A not infrequent comment, often presented like a big gotcha, was that since
Optional can be
null as well, you still need to do a
null check for the
The parameter clearly isn't meant to be
null so if it is, there's nothing to be done except throw a
You don't need a
null check for that - just call a method on it.
That said, passing or returning
null for an
Optional is really bad.
There's never a situation where this is acceptable.
On the one hand that means that if it does happen, it's automatically a bug and one that's easy to fix (probably just replace with an empty
Optional), which is way simpler than if you first have to figure out whether
null may legally represent absence in this case.
On the other hand, it makes it very easy to educate your colleagues accordingly.
So, don't do
null checks for
Optional arguments unless you store them away (e.g. in fields).
Optional isn't serializable.
Which sucks when using it as parameter or return type in methods that are called by, for example, RMI (not the most common use case anymore, though).
To work around that, you first need to create a serializable wrapper, which is fairly straightforward.
You can then use that wrapper in the methods that need it, which adds an additional wrap/unwrap call on each end - not great, but not the end of the world, either.
If you're considering using
Optional for fields, you can apply the serialization proxy pattern to replace the
Optional with the value it wraps in the class' logical representation.
The blog post on the serializable
Optional wrapper describes that as well.
If you already have a serialization proxy set up, this change takes a few minutes.
If you don't, check out item 90 in the third edition of Effective Java or this post for why and how.
And since we're talking about Effective Java, give item 85 "Prefer Alternatives to Java Serialization" a read. I quote:
In the words of the computer named Joshua in the 1983 movie WarGames, "the only winning move is not to play." There is no reason to use Java serialization in any new system you write.
Yeah, this one is still not looking great. I checked a few and, frankly, was a bit shocked by the lack of progress.
In JPA, entities can't have
Optional-bearing fields, but if field injection is configured, at least the accessors can bear
Spring Beans and Spring Data JDBC, on the other hand, allows such fields in configs and projections, respectively.
Likewise, Jackson supports them out of the box, but GSON and Moshi need custom adapters.
Map Struct has an open issue for this, which was recently added to the 1.6 milestone.
So, yeah, if you're working with a framework and like to use
Optional a lot, check compatibility for your use case beforehand.
This one didn't come up in the conversation, thankfully, but it is often trotted out as an argument for
Optional's API being "just stupid":
Optional have three static factory methods—
ofNullable—when the last one clearly suffices?
In short: to express intent.
If the method returns an
Optional, but you don't have a value, return
If, on the other hand, you have a value that you expect to never be
Optional::of to express that assumption and fail early if it turns out to be wrong.
ofNullable if you don't know and don't care whether the instance you want to wrap is
Since we're talking about expressing intent, that's the main upside I see with
As one comment succinctly put it:
The importance of
Optionalis not to handle
null, but to explicitly express the intention of nullability.
Optional is used consistently, either for all returns that allow absent values or in all other places as well, it becomes much more than just an API to handle absence.
It becomes the sole and unmistakable marker for all cases in which a value can be absent.
Because the problem with
null isn't the
if statements, it's figuring out whether
null was a legal value in the first place.
Once that's settled, fixing it is usually easy.
And when consistently using
Optional for every absent return type or even any absent instance, in all those cases the legality question is already settled and all that remains is the easy part.
I wrote an entire article (and another one) about this, if you like more detail.
(Wow, seven and six years old, respectively. Tempus fugit.)