Tony Hoare, inventor of the null reference, apologetically called it “the billion dollar mistake”.

The problem with null is not that it was invented back in 1965, but that we are still struggling with it 50 years later. Any Java developer has probably seen and written code like this:

String result = service.find("id");
String value;
if (result != null) {
    value = result.trim();
} else {
    value = "<absent>";
}
System.out.println("Value: " + value);

In fact, most Java monoglots have probably experienced code like this so many times, they don’t even see the problem anymore. And yet, with Java 8, they have to deal with a solution: the Optional type.

I recently came across this article on Voxxed: Embracing the Void: 6 Refined Tricks for Dealing with Nulls in Java. It gives a nice rundown of strategies around the null reference. Sadly, it discourages the use of Java 8’s Optional. In this post I will explain why and how you should use Optional…

Types to the rescue

One way to re-write the above snippet using Optional could look like this:

Optional<String> result = service.find("id");
String value;
if (result.isPresent()) {
    value = result.get().trim();
} else {
    value = "<absent>";
}
System.out.println("Value: " + value);

The service returns an Optional<String> and the caller is working  around it. From a complexity point of view, the change is argueably not much of an improvement: We still need a mutable value variable, and the overall logic is roughly the same. And yet, the expressiveness has improved. The signature of the service has clearer semantics now. Originally, the caller of the service could not distinguish between “the implementor of the service made a programming error” and “the value has not been found” – both were resulting in null. This is possible now and – even better – the type system forces the caller to deal with absent values.

But wait, there’s more

Now that the semantics are straight, let’s make the value variable immutable and eliminate the if .. else construct:

Optional<String> result = service.find("id");
final String value = result.orElseGet(() -> "<absent>").trim();

This code is safer, more expressive, and more concise than the original version, but the () -> "..." clutter makes my Scala heart cringe, so I’m offering yet another variant:

Optional<String> result = service.find("id");
String value = result.map(String::trim).orElse("<absent>");

We are taking advantage of the fact that Optional behaves like a collection: If it is absent, it behaves like an empty list, the call to map is never executed, and the result is an empty Optional (which we handle via orElse). If the result is present, it behaves like a list with one element, which we trim and return.

Wrapping legacy code

Imagine that our original service interface is actually coming from a 3rd party library. You can’t simply change all the signatures to properly return Option<T>. But you can wrap the service calls and create yourself a null-safe world:

Optional<String> result = Optional.ofNullable(legacyService.find("id"));

Unboxing a cat in a box in a box

And finally: The holy grail of functional programming, flatMap.

Let’s start with a simple User which has an optional middle name:

interface User {
    String getName();
    Option<String> getMiddleName();
}

Looking up the user via service is straightforward, but look at all those types:

Optional<User> result = service.find("id");
Optional<Optional<String>> middleName = result.map(User::getMiddleName);

The outer Optional is the result of the map call (presence/absence of User), while the inner Optional is wrapping the middle name value.

String middleName = result.flatMap(User::getMiddleName).orElse("Ada");

So next time you can’t remember the semantics of flatMap, just think of all those cats stuck in their nested boxes:

(via @channingwalton)

I highly recommend The Neophyte’s Guide to Scala – Part 5: The Option Type for anyone interested in further details.

In Defence of Java 8’s Optional: Why and How to Use It

| Java Language| 6,171 views | 7 Comments
About The Author
- Java & Scala software craftswoman, mother of two, mountain biker, dog lover, guitar player, country bumpkin — in no particular order. Read more on my blog: http://netzwerg.ch/

7 Comments

  • Richard
    Reply

    We’re starting from the wrong spot here, by starting with commonly accepted null handling patterns we’re subjecting ourselves to an anchoring bias – *any* proposed solution would look good in comparison.

    The question we should be asking is: do Optionals give us any additional safety or expressiveness over run-of-the-mill collections? In my view they don’t. From Daniel Westheide’s guide (emphasis mine):

    > More precisely, you may think of it as some kind of collection – some *special snowflake of a collection* that contains either zero elements or exactly one element of type A.

    Now, I value simplicity so I have a high bar for snowflakes. Does the snowflake bring enough to the table to justify it’s existence? What Optional brings to the table is having an upper bound of a single element. But, this constraint carries no additional safety – what if your service allowed two users with the same id? For the system to be consistent the constraint needs to be enforced at write time, not at read time, so at best Optional is just repeating a piece of logic held elsewhere.

    So is it more expressive? Well, yes, but in practice how is the consumer going to behave any differently. Mapping, filtering, reducing, it’s all going to work the same way anyway, so what has the extra expressiveness actually brought us? I guess callers can now afford to be a bit more cavalier about realising the collection if they know it’s only going to contain at most one element, but that’s not a practice I would be encouraging lightly anyway.

    Where you want a single value, just pass or bake your defaults into your service and return a single value – guaranteed. And when you don’t care about having a single value, an off-the-shelf collection already does the job admirably.

  • robert_annett
    Reply

    I’m not sure that:

    String value = result.map(String::trim).orElse(“”);

    is any better than:

    String value = (result!=null) ? result.trim() : “”;

    Using the ternary makes it obvious that this is a simple check to deal with null. I find the ‘map’ method quite distracting from what the line of code is actually doing (providing a default in the event of a null).

    • tddmonkey
      Reply

      I prefer the:

      result.orElseGet(() -> “”).trim();

      syntax as to my eyes it makes more logical sense, but in a codebase that follows a strict use of Optional, as the article states, you as the API designed are making it clear that “this method call can return a value, or not”. In the places that Optional is not present you are effectively saying “this is guaranteed to never return null” and hence null checks can be skipped. This is a lot more obvious in Scala where everybody just seems conditioned to using Optional.

    • Simon
      Reply

      I agree… I’m not very comfortable with the map-orElse construct here, because it’s much less clear what it does. The second form is *obviously* dealing with an optional/nullable value – the first is something you have to think about a bit.

      Of course, if I have a lot of those, I usually end up just calling trim(result), and using a null-safe trim function from a String util class. Not the kind of thing that makes OO purists happy, but it’s usually preferable to leaving inline null checks everywhere (and Optional is just a variant of a null check, really).

  • Paolo Denti
    Reply

    Optionals, in a politically correct sentence, are just syntactic sugar: same meaning, different syntax (really bad sugar anyway, i would prefer the bitter taste)

    In a more realistic way, Optionals are just avoidable complications for such a fundamental java paradigm.

    null checking in java owns a semantic since the very first day. usually, by contract, every method/service returns null when something is not found, not existing, “not something” in a normal and consistent flow (errors are managed by exceptions). And it is simply declared in the method/service documentation. Why should we change such a basic behaviour?

    And why should i wrap a legacy service in order to “create yourself a null-safe world”? If i get a NullPointerException it is because i am writing bad code. Do we really need an Option to become “better coders”? If the answer is yes, do we also need some DifferentFromZeroNumber new object in order to avoid ArithmeticException? Or do we also need an impossibleInfiniteForLoop new java construct in order to avoid bad coding?

    • Yannick Majoros
      Reply

      The difference between a method returning null for elements that aren’t found, or Optional.empty() for the same, is that you just can’t forget the check. You will need it if you want your code to even compile, as stated in “Types to the rescue” in TFA.

      Some other example…

      Optional ownerOptional = find(“Roger”);
      Person owner = ownerOptional.orElse(defaultOwner);

      With a null check, it’s easy to have a NPE (thus at runtime) rather than a safer compile-time check:

      Person owner = find(“Roger”); // easy to forget to check for null…

      It’s even more obvious if you use multiple map() operations, ifPresent(() -> {…}) etc.

      The “basic behaviour” just can lead to errors that are easy to check at compile time with Optional.

      Is it more verbose? In dumb examples, yes. In real code, you start writing thing differently, and it becomes more succinct over time in my experience. Things like building complex CriteriaBuilder requests with many optional (!) parts come into my mind. Combining map() and ifPresent() can help you build them easily, and still maintain in in a readable way.

  • lukka pekk
    Reply

    …Originally, the caller of the service could not distinguish between “the implementor of the service made a programming error” and “the value has not been found” – both were resulting in null… what a crap :/ “Programming errors” are called exceptions…

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>