Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

David Schmitz
David Schmitz

Posted on

     

Save the Optional, stop using isPresent

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.

Usingnulls we write code similar to this:

Useruser=repo.findOne("id");if(user!=null){Addressaddress=user.getAddress();if(null!=address){returnaddress.getStreet();}else{return"";}}
Enter fullscreen modeExit fullscreen mode

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"";}}
Enter fullscreen modeExit fullscreen mode

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.

Gift box

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.

Mapping to a ball

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:

Nothing from nothing

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();}}
Enter fullscreen modeExit fullscreen mode

First of all, letfindOne returnOption<User> instead ofnull:

Option<User>user=repo.findOne("id");...
Enter fullscreen modeExit fullscreen mode

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)...
Enter fullscreen modeExit fullscreen mode

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)...
Enter fullscreen modeExit fullscreen mode

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("");
Enter fullscreen modeExit fullscreen mode

Which leaves us with the final version:

repo.findOne("id").flatMap(User::getAddress).map(Address::getStreet).getOrElse("");
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
alebianco profile image
Alessandro Bianco
  • Joined

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.

CollapseExpand
 
koenighotze profile image
David Schmitz
CTO, coder, and architect at Senacor Technologies in Germany. Coding in Java, JavaScript, Go, whatever gets the job done. Get in touch via @koenighotze on Bluesky.
  • Location
    Düsseldorf
  • Education
    Master of Science of Computer Science
  • Work
    CTO 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".

CollapseExpand
 
phouchens profile image
Perry H
Just an average console cowboy.
  • Work
    Software Engineer
  • Joined

"Each time Option is used like this, a microservice dies in production."
😂😂
Nice article!

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

CTO, coder, and architect at Senacor Technologies in Germany. Coding in Java, JavaScript, Go, whatever gets the job done. Get in touch via @koenighotze on Bluesky.
  • Location
    Düsseldorf
  • Education
    Master of Science of Computer Science
  • Work
    CTO at Senacor Technologies
  • Joined

More fromDavid Schmitz

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp