Most functional programming languages offer a concept calledOption orMaybe to deal with the presence or absence of a value, thus avoidingnull
. Java 8 introducedjava.util.Optional
, an implementation of theMaybe type for Java developers. Sadly, due to its flexibility,Optional
is often misused, be it because the developer does not understand its power, or be due to lack of background in functional programming.
In this post I want to highlight a common pattern of misusingOptional
and how to fix it.
Note that instead ofjava.util.Optional
, I will use the VavrOption
instead.Vavr is a lightweight library that brings Scala-like features to Java 8 projects. It focuses on providing a great developer experience both through consistent APIs and extensive documentation. Seethis short overview of how and why optional can help you. Head over tohttp://vavr.io if you want to know more.
But, everything here applies to either implementation.
A real world example
I want to start with a typical example that we can use as a refactoring candidate.
Let's consider the following use case:Load a user using a repository. If we find a user we check if the address is set, and if so, we return the street of the address, otherwise the empty string.
Usingnull
s we write code similar to this:
Useruser=repo.findOne("id");if(user!=null){Addressaddress=user.getAddress();if(null!=address){returnaddress.getStreet();}else{return"";}}
Urgs. This is what I call aCascading Pile of Shame.
Fixing this is easy, just useOption
:
Option<User>opt=Option.of(user);if(opt.isPresent()){Option<Address>address=Option.of(user.getAddress());if(address.isPresent()){returnaddress.get().getStreet();}else{return"";}}
Right?
Wrong! Each timeOption
is used like this, a microservice dies in production. This fix is basically the same as above. Same complexity, same _Cascading Pile of Shame.
Instead we use themap
operator.
Map - the Swiss army knife of functional programming
map
is your friend when usingOption
. Think ofOption
as a nice gift box with something in it.
Suppose you are a good programmer and wrote your code Test-First. You get a gift box with socks.
But who wants socks? You want a ball. So youmap
a function to the gift box, that takessocks and transforms them to aball. The result is then put into a new gift box. Your birthday is saved through the power of monads.
What if you are a bad coder and do not write unit tests at all? Well, then you won't get any nice socks. Butmap
still works fine:
If the gift box is empty, thenmap
won't even apply the function. So, basically it is "nothing from nothing".
Fixing things
So going back to the original problem, let's refactor this usingOption
.
Useruser=repo.findOne("id");if(user!=null){Addressaddress=user.getAddress();if(null!=address){returnaddress.getStreet();}}
First of all, letfindOne
returnOption<User>
instead ofnull
:
Option<User>user=repo.findOne("id");...
Since the user's address is optional (see what I did there ;)User#getAddress
should returnOption<Address>
. This leads to the following code:
Option<User>user=repo.findOne("id");user.flatMap(User::getAddress)...
WhyflatMap
...well, I'll leave that as an exercise. Just remember, thatUser#getAddress
returnOption<Address>
and think about, what would happen if you usedmap
?
Now that we've got theOption<Address>
we canmap
again:
Option<User>user=repo.findOne("id");user.flatMap(User::getAddress).map(Address::getStreet)...
Finally, we only need to decide what to do if everything else fails:
Option<User>user=repo.findOne("id");user.flatMap(User::getAddress).map(Address::getStreet).getOrElse("");
Which leaves us with the final version:
repo.findOne("id").flatMap(User::getAddress).map(Address::getStreet).getOrElse("");
If you read it from top to bottom, this is as literal as it gets.
repo.findOne("id")// Find a user.flatMap(User::getAddress)// if an address is available.map(Address::getStreet)// fetch the addresses street.getOrElse("");// otherwise use the empty string
Summary
I hope this short post illustrates the usefulness of Vavr and itsOption
abstraction. If you remember one thing only, then please let it beDo not useOption#isPresent
orOption#get
, themap
is your friend.
Vavr as a library offers many amazing extensions for object-functional programming in Java, even for brownfield projects. You can leverage its utilities where they make sense and need not migrate to Scala or similar platforms to reap at least some benefits of functional programming.
Of course, this is all syntactic sugar. But as any good library, Vavr fixes things, the core JDK cannot take care of so easily without breaking a lot of code.
Future posts will cover its other amazing features like pattern matching, property based testing, collections and other functional enhancements.
Top comments(3)

I find the 'exercise for the user' quite confusing since no clear signature for those methods are given and the return type changes between the examples.
Anyway, if anyone is wondering,flatMap
is used becausegetAddress
returns anOption<Address>
in one of the examples.map
is used instead with 'getStreet' because it returns a plainString
.

- LocationDüsseldorf
- EducationMaster of Science of Computer Science
- WorkCTO at Senacor Technologies
- Joined
Good point, thank you. I have added a small extra sentence, that hopefully reduced the confusion.
To add to your answer:flatMap
expects a mapper that returns anOption
.map
on the other hand wraps the result of the applied mapper function into eithersome
ornone
.
In short:map
creates a new "Gift Box" andflatMap
reuses the "Gift Box".
For further actions, you may consider blocking this person and/orreporting abuse