Java Optional: Is It Useful?

Introduction

Java Optional is a container class that encapsulates an optional value, which was introduced by Java 8. However, I still see lots of colleagues not leveraging it in their work. Maybe it’s too hard to use? Or maybe we don’t see the the beauty of this feature? Let’s dive into it.

What’s wrong with following code?

Let’s take a look the following Java code:

Optional<Dog> dogOption = Optional.ofNullable(dogService.getDog(...));
if (!dogOption.isPresent()) {
    ...
}

Sometimes we’ll see nested Optional, which requires nested if statements to check the value existence. So why do we need Optional instead of simply a null check? Let’s first look at the APIs this class is exposing.

APIs

Create Optional object

Optional<String> empty = Optional.empty(); // create empty Optional object
Optional<String> stringOptional = Optional.of("abc"); // the argument cannot be null
Optional<String> nullableStringOptional = Optional.ofNullable(null); // the argument can be null

Check If optional object has value

Optional<String> stringOptional = Optional.of("abc");
assertTrue(stringOptional.isPresent());

Optional<String> nullableStringOptional = Optional.of(null);
assertFalse(nullableStringOptional.isPresent());

Provide default value

There’re 2 ways to provide a default value if the value inside Optional is an empty value: orElse(T value), OrElseGet(Supplier<? extends T> supplier).

String name = Optional.ofNullable(null).orElse("default name");
// or
String name = Optional.ofNullable(null).orElseGet(() -> "default name");

What’s the difference between the 2? Let’s say the optional has a non-empty value:

String name = Optional.ofNullable("abc").orElse(getDefaultName());
String name = Optional.ofNullable("abc").orElseGet(this::getDefaultName());

In this example, getDefaultName() will be executed by orElse(), but won’t be executed by orElseGet().

Throw exception if value is empty

String name = Optional.ofNullable(null).orElseThrow(IllegalArgumentException::new);

Get value from Optional object

String name = Optional.of("abc").get();

But if Optional has empty value, will throw NoSuchElementException when calling get()method.

Filter

Optional<String> stringOptional = Optional.of("123");
assertTrue(stringOptional.filter(s -> s.equals("123")).isPresent());

The filter method will return non-empty optional if the value meets the predicate, and empty optional otherwise.

Map

If the value inside Optional is not null, will map the value to something else. Otherwise, return an empty Optional object.

int size = Optional.of("123").map(String::length).orElse(0);
assertEquals(3, size);

Flatmap

It’s similar to map, but won’t automatically apply an Option wrapper around the value. Instead, it’s asking the parameter function to return an Option object. Thus, this method is good at transform Optional<T> to Optional<R>.

How to use Optional?

We are trying to get a nested value, say a state name within an address. If we do it in non-optional way and need to avoid null pointer exception, the code will be like following:

User user = getUser(userId);
if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        State state = address.getState();
        if (state != null) {
            return state.getName();
        }
    }
}
throw new NoSuchElementException();

If we change the code to follow the “Optional” way, it will be as following:

Optional.ofNullable(getUser(userId))
        .map(user -> user.getAddress())
        .map(address -> address.getState())
        .map(state -> state.getName())
        .orElseThrow(NoSuchElementException::new);

So if we use Optional, we see the code is more concise and elegant. Even if user, address or state is null, we don’t do any null check, and still, this code won’t throw null pointer exception.

A question you may ask is: If user is null, will the following map statements be executed? The answer is yes. Let’s take a peek at source code:

public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (isEmpty()) {
        return empty();
    } else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

As we can see, if the value is empty, the map method will return an empty object (null) to keep the chain operations proceeding.

Summary

This article talks about how to properly use Java Optional. Optional provides a way to clearly represent “no result” for method return types. Try to leverage its API and avoid explict null check. Of course, don’t overly use it such as mark all class fields as Optional.

Scroll to Top