Most Java developers believe that Oracle introduced Optional
to protect you from running into NullPointerExceptions
.
That's wrong on almost all levels. Let's have a look why Optional
has been invented, and why so many programmers believe it solves the NPE problem, and why that's wrong. It's just a misconception you can't evade when you're coming from a language like Haskell.
Is it wrong to use Optionals?
First of all, this article isn't about flame wars. When we're talking about using if
vs. Optional
, there's no right or wrong. Both approaches work for the CPU, and while most developers cope better with the traditional if
statement, there are also many developers who consider chaining more readable.
The only thing that's not debatable is performance. CPUs have been designed with traditional if
statements in mind (or something similar), so checking conditions and branching to a new line is a use case they are optimized for. My in-depth performance analysis shows that there's a factor 10 performance penalty of using Optional
. When the code runs very often - something like ten thousand times - the JIT compiler does an excellent job optimizing the code. Even then, there's a noticeable 20% performance disadvantage.
If other words, if
statements are good for your carbon footprint. From this perspective, replacing if
statements by Optional
is evil. But it's not wrong. It just consumes more energy and time.
Run the test yourself!
This article is about when and when not to use Optional
. Conditional code execution is only a small part of it. But because it's such a popular trend to replace if
statements by Optional
, let's spend one final paragraph on it.
Ask your team to write an algorithm without using if
statements. If they write it fluently, go for Optional.orElseGet()
, Optional.filter()
and Optional.map()
. But I bet interesting discussions will pop up. What the difference between flatMap()
and map()
? Can we use orElse()
, or does it have to be orElseGet()
?
During my research I've found an interesting example to make you think:
String name = computer.flatMap(Computer::getSoundcard) .flatMap(Soundcard::getUSB) .map(USB::getVersion) .orElse("UNKNOWN");That's two lines using flatMap()
. Why is the third line different? Why does a simple map()
do the trick in the third line, but not in the first two lines? And why do we need three times a lambda expression (or a method handle), but the last line is happy with a simple String
?
I suspect using Optionals
for such simple tasks adds a lot to the mental effort required to read the code. Mind you, human language embraces if
and else
, too: "If the weather is fine, I'm hiking in the mountains. Otherwise, I'm going to the cinema".
Introducing the Maybe datatype
Do you know Haskell? That's a functional programming language with a unique but powerful programming style. Among other things, it has two data types called Maybe
and Just
.
Imagine your corporate address list. Everybody has a first name. That's Just String
. But not everybody is willing to tell you how old they are. So their birthdate is Maybe DateTime
. It may have a value, but maybe it's just Nothing
.
Does that sound familiar? Yes, so far it's exactly the same as Optional
. The difference is that Maybe
is part of Haskell's type system. Optional
is a class in a library.
This means that even if the syntax is more or less the same, the semantics are wildly different. The Haskell compiler knows the difference between Maybe
and Just
. The Java compiler does not. So it misses many mistakes the Haskell compiler detects.
Optional in Java
Optional
has been invented to take the sting out of dealing with null values.
But that doesn't mean declaring a variable as Optional
eliminates the dreaded NullPointerException
. Quite the contrary:
You still have to deal with null
values. It's just a convention that Optionals
never are null. That's a powerful convention, one that works most of the time because the Optional
keyword makes you aware of the problem. But still, it can be broken. Every Optional
is an object, so null
is a legal value.
Plus, even if everybody recognizes the convention, you've just replaced the NullPointerException
with a NoSuchElementException
. That gets you nowhere.
It's the same old story. Inventing a library to solve a problem that needs to be addressed in the language only makes things worse.
What Oracle says
Here's the catch. While it's true that Optional
has been invented to take the sting out of dealing with null values, it has not been invented to get rid of the NullPointerException
. Stuart Marks, member of the Java language design team, clearly says they've invented Optional
to deal gracefully with null
values in streams. He also say they were very surprised when developers started using Optional
in many other scenarios.
But then, Oracle is a big company. Big enough to send contradictory signals. They're also trying to convince us it's a good idea to get rid of the if statement.
Beauty is in the eye of the beholder.
When and how to use Optional
I haven't answered the question yet, have I? I've found two well-thought articles I try to distill. I'm talking about A look at the Optional datatype in Java and some anti-patterns when using it and Nothing is better than Optional.
- Generally speaking, it's a good idea to use
Optional
in streams. That's a lot better than using null value. - But it's even better to avoid using empty values in streams altogether. Just filter them away. The nice thing is that
filter()
copes with null values. - Wrapping primitive data types in
Optionals
is tremendously inefficient. Don't do that, it just ruins your carbon footprint. Primitive data types can't be null, anyways. - The real problem is that the average hurried programmer always forgets to add the necessary checks.
Optional
reminds them. But an annotation@NonNullable
does that, too, at a lower price. It's even supported by your IDE. I've coverd the nullness annotations in depth in my previous article. - If you're using an
Optional
for a method parameter, think twice. The method must check whether the optional parameter is there. Maybe it's better to write two different methods. Not always, but optional parameters are a code smell.
Wrapping it up
There's nothing wrong with Optional
. Use it. But use it wisely. If you feel obliged to use it, think twice. Do you want to use it because it's fancy? Because your peers told you so? Because it's fashionable? Because someone told you it's faster?
These are the wrong reasons to use Optional
. They tend to convert your clean code into a messy ball of mud.
You must find your programming style. Every once in a while, jot down an algorithm using Optional
, and the same algorithm using a traditional coding style. Return a week later. Or ask a friend. Which version reads more naturally?
Optional
is a powerful tool, so you'll find many use cases. It's just not as powerful as Haskell's Maybe
data type. In Haskell, using Maybe
isn't optional. Java operates at a lower abstraction layer. It's closer to the CPU, so using Optional
usually is optional.