Use Optional to Shield Yourself from Bad API Design

There are many articles that explain the broad applications of Java 8’s new Optional class (e.g., Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional!), but they focus on how authors can use Optional to improve the design of their own APIs. I want to talk about how Optional can be used to protect us from the APIs we didn’t write.

One thing we can mitigate are methods that usually return a Collection (or array), but will instead return null when there are no items to put into the Collection—ideally, they would return an empty Collection. I deal with this most often when calling HttpServletRequest#getCookies() which returns “an array of all the Cookies included with this request, or null if the request has no cookies”.

What I used to do in this situation is this:

// Sub-optimal way to handle null return value.
// Don't do this.
Cookie[] cookies = request.getCookies();

if (cookies != null)
{
  // Go about your business...
}

But that null check is bumming me out! We can use Optional here to handle the null case for us:

// Optional makes it better.
// Use this.
Cookie[] cookies = Optional
    .ofNullable(request.getCookies())
    .orElse(new Cookie[] {});

// Go about your business...

Just to make the point: suppose the return value of getCookies() was Set, for example, then the argument of orElse would be Collections.emptySet().

This makes things more elegant when we stream the array. For example, we can look for the value of a particular Cookie like so:

// Elegant.
// Use this.
String cookieValue
    = Arrays.stream(Optional
        .ofNullable(request.getCookies())
        .orElse(new Cookie[] {}))
    .filter(cookie
        -> "cookieName".equals(cookie.getName()))
    .map(Cookie::getName)
    .findFirst() // <-- another Optional
    .orElse("default");

The above code snippet is better than the next, wouldn’t you say?

// Ugly null check, boo!
// Don't use this.
Cookie[] cookies = request.getCookies();
if (cookies != null)
{
    String cookieValue
        = Arrays.stream(request.getCookies())
        .filter(
            cookie -> "cookieName".equals(cookie.getName()))
        .map(Cookie::getName)
        .findFirst() // <-- another Optional
        .orElse("default");
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s