Every Java programmer knows
Optional type. If someone doesn't, shame on her/him. In this post I'll show you the power of
and will explain in details where does this power comes from.
First of all,
Optional is a Monad. There will be more about Monads in another post but for the sake of this article I'm highlighting some
Every Monad has factory method:
Every Monad is a parameterized container:
Every Monad has a mapping method taking function accepting monad's type and returning Monad:
Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
- Monads are immutable
Finally and last but not least Monad is a Monad only if it obeys three laws: Left identity, Right
identity, Associativity. You must take it on faith now,
Optionalobeys all of them.
Optional is a Monad indicating whether it holds some value or is empty. It is very useful replacement of
null and we should
nulls at all costs because
null is a huge code smell. There is not enough place here to express how smelly it is but we
can Google it: java null criticism,
Why you are using
Is it happening to you more often than very rarely to write code like this?
If it does, please stop doing this, you are hurting all Monads. The fact that
Optional.isPresent() is available doesn't mean that we
should use it except exceptional situations. If we would like to follow monadic conventions, above code should look like this:
These code snippets lengths difference is striking, isn't it? The
calculateSalary method signature is changed too, the return type is
now. It might seem to be minor difference but is quite significant. We don't have to document internal implementation behavior of returning
in case of not found employee, we expressed it by method's signature. We inverted the control making caller's code to decide what to do in such case,
whether pass some other
Optional further or maybe throw an exception. We just don't care here and this is good.
Optional to early, let it flow through your code for the sake of clarity and safety.
Map and filter it
Next case are the heavily underused
filter methods. Let's say we have string values stored in the map and expose it via
some service class. Additional features provided by this class is the validation of the keys used to obtain values and some method used to decorate
returned values. This is traditional example implementation.
This series of
if statements can be easily replaced with
We can go further and get rid of blank key check before
We are allowed to use
cache::get in line 10 because
Function<? super String,? extends String> there. It means function accepting any superclass of
returning any subclass of
cache.get() method reference with signature
I personally find it much more convenient and descriptive to apply filters and transformations in this way. If you don't, maybe next example will be more convincing.
Our task it to write method parsing text provided by user to percent format. To make things more complicated, users are from country using comma as decimal separator. This is simple implementation.
And this is equivalent implementation using
One might say that this is semantically the same code, only syntax vary. Well, that's true, but there is one difference you might not spot. In first
example we did several intermediate assignments in lines 7, 9 and 11 to increase readability. It required additional mental work to think about clean variables names. When we perform
series of transformations with
Optional.map() we can avoid this job.
Additional thing to point out is the weird exception thrown in line 19. This is dead code actually so it
doesn't matter what exception is declared to be thrown, it will never be thrown anyway. It will never be thrown because any problematic value will cause
in line 11. It doesn't look good and would trigger questions and discussions. The reason is impure functions
usage and bad API design. The impure function is
BigDecimal constructor. It throws
NumberFormatException in case of non number
string provided as an argument. Pure functions always return value and never throw exceptions. In previous examples we also saw the rule applied: Don't
Optional to early
. Let's change the parser code to return
Optional and use pure functions.
This time we use pure
createBigDecimal(String str) function to create
Optional.empty() in case of invalid number representation. To handle nested
Optional mapping, we use
in line 19, otherwise the type returned by expression would be
One thing to notice is that we don't know the reason why our
parsePercent function returned empty
Optional: because provided
null, blank string or invalid number representation? The calling code knows only whether there is some value or there is now
value returned. There is handy way of handling such cases with ... of course another Monad named
Try. There will be another post about
Monad but here below is the same code using
Try from Vavr library.
Optional based code similarities are really striking, so much that it's easy to overlook the differences by just
reading the code. Furtunately, the compiler will spot the difference. The caller code will not differ much as well. Used
provides bunch of methods very similar to those supplied by
We can even convert the
toJavaOptional() method if we don't care about error details.
This is the true power of Monads: we use all of them in the same way. If we know how to use
Optional, we know how to use other Monads.
Handling missing nested values
To let you feel what I mean by deep nested missing values, I'd like to recall you old Java XML W3C DOM API. I find it most tedious to work with,
because methods it provides, very often return
null values. Additionally, XML's Document Object Model is the tree of nodes that can be really
Let's imagine that we have users database stored in XML file like this one:
As we can see, some users have address specified and some do not. To get user's street from address, we have to access it via quite long path:
find node named
check whether child node
idhas expected text content
addresschild node if
streetchild node if
I'll let you you imagine how boring and error prone native DOM API use would be. To mitigate this, we created several functions operating on
Optional as return value heavily.
First, we need the easy way to iterate over child nodes. Unfortunately,
NodeList returned by
Lets' create our own implementation.
If we have
Iterator, we can use
Stream created from it. This is function creating
Next, we need some predicates to check various node properties.
As we can see, there is
Optional used all the time. This is due to the fact, that
Node methods can return
Node type. For example,
getAttributes() methods called on text node always return
of() factory method because we don't accept
null in predicates. In this way, we will fail
Then we need functions finding nodes. Here they are.
Finally, if we have node finding functions, we can easily create additional useful predicate checking whether given
Node has child matching
Let's see our toys in action.
This code is totally safe (unless we left some bugs) and way easier to write and comprehend. We created custom
from primitive ones for convenience and used in line 3.
This predicate checks, whether given node name is
user and has child node named
id with text content equal given
Below test code is identical to previous on, only assertion differs because we don't expect Mary Jane (having id 2) to have address specified at all.
What if we ask about non existing user? No problem.
There is no user with id 3 and the test doesn't fail.
And what about using attributes not present in our XML file? No issues at all.
There is no
user node with attribute
name and the test passes.
As we can see, by writing less then 100 lines of code using
Optional heavily we made our life with XML DOM API easier and safer.
Eager and lazy alternative value
One of the frequent usages of
Optional is to get alternative value in case of empty
Optional. We do this via
orElseGet(Supplier<? extends T> other) method. It's common mistake to use
should be used. The difference between these methods is that
Supplier, so actual alternative value is evaluated
lazy, only if is needed meaning when
Optional is empty. Java evaluates methods arguments before method body execution, we call it eager
evaluation. Improper selection of alternative value provider may result in unexpected behaviors, like in below example.
Let's say, we have repository interface and implementation backed up by some persistence technology. We express missing lookup by primary identifier by
Optional from appropriate method.
getDefault in line 5 returns the
Entity by hardcoded
id value and can be
used if the one we were looked for was not found. It also prints debug message for presentation purpose. Here below is simple test showing the difference
between eager and lazy evaluation of
Optional alternative value.
orElseGet alternative value provider works in this way because it expects
Supplier is the
functional interface with single abstract method
T get(). This is all we need to obtain default value, just call
return its result. This is exactly what
orElseGet method does. Without calling
Supplier.get() nothing happens. On the other
orElse expects default value which is evaluated before execution of our code using
Optional. If we want to return simple
default value, like
Optional.orElse() is perfectly legitimate.
Whenever additional operations are needed to provide default value in case of empty
orElseGet. If the default
value is just simple object or constant, use
If you think that behind
Optional's powers stays some sophisticated code ... you are wrong. The concept and implementation is actually very
easy. The best way to see what is happening under the hood is to implement our version of
Optional. Let's name it
distinguish it from Java's
Optional and Scala's
Maybe is the generic container, so it has to be parameterized.
If we think about very nature of
Maybe it's just either something or nothing. We can easily design it as abstract class with only
Nothing. We don't want to allow any other implementations to exist, so let's make
Nothing constructors private.
Nothing will be static and final inner classes of
Just should hold some value, let's add the value placeholder to it.
There is only one and only one
Nothing, we can create singleton for it.
If you wonder why
NOTHING singleton is of unbounded wildcard type, the answer is simple:
Nothing holds no value, it's type is
meaningless and can be discarded.
Next, we need to have the ways to create
Nothing instances. We can't just construct them, because their constructors
are private. Let's write appropriate factory methods in
Nothing we always return our singleton instance
NOTHING. We do cast it to
Maybe<A> to avoid
unnecessary unchecked cast warnings. Remember?
Nothing holds no value and has no type actually, it's safe to cast it to
Just on the other hand must hold the value,
null is not acceptable, hence we validate
factory method argument against
null references in line 7.
We would like to create
Maybe without knowing if it's nothing or something too. Let's craft the method doing so.
This is what we have so far.
We may have a value in
Just but we don't know how to check it. Let's add
Maybe.isPresent() abstract method and implementations.
Obviously, implementation will be simple but completely different.
And this is
Let's quickly go through value accessors declaration in
Maybe, they are not very interesting anyway.
And these are taken from
We do completely opposite things in
Nothing respectively. This kind of symmetry is often present in functional
programming where Monads were born.
Finally the most important methods:
map. If you wonder why
flatMap is in the first place ... the reason
map can be implemented using
Let's start with
map because it's easier to comprehend. Its signature is as follows:
map(Function<? super A,? extends B> f).
f argument is named after
Function. What this method does is: it takes
given mapping function and returns new
Maybe containing result of this function applied to
Nothing implementation is simple, there is no value to apply given function to, we have only one choice, return
Just.map() implementation is very straightforward too:
flatMap behavior is harder to explain and easier to write: take
Maybe with some value in it and apply mapping function
Maybe to original
Nothing, no surprises here.
Just.flatMap() simply returns the result of given function applied to wrapped value.
flatMap is the feature making
Maybe a Monad, actually
map is redundant because can be implemented using
It is usually implemented separately for performance reasons.
mapViaFlatMap is a method implemented in our abstract
Maybe class. Presented above code is very functional. We can read is as: take
f function, compose it with
Maybe::maybe, the composed function is
maybe(f(v)) with signature
Function<? super A,Maybe<B>>, and pass it to
We can also implement it using lambda expression if we find it easier to grasp:
If you don't know what function composition is, you may find my post Functional programming magic quite
helpful. If you would like to see full
Maybe source, click here. You will find side
ifPresentOrElse method there.
That's it, about 100 lines of code and we have fully functional
Optional replacement. We reinvented the wheel, there are plenty
implementations available but our understanding of
Optional is much better thanks to this exercise.
We know how to use
Optional. We know how simple and easy to implement concept it is. This is always like this: great power lies in
simplicity. I personally can't imagine my life without some kind of
Optional in my programming toolkit and I hope that after reading my post
you will not be able to do so either.