Java 8 introduced Optional which serves as a container for values abstracting out the presence or absence of values. In this article I will be showing how using it helps in writing null safe code.

Let’s say you have to use an interface to write some logic and you see the below interface definition.

public interface OrderService {
  Order findOrderWithId(long orderId);
}

What is the first thing that comes to your mind? Most probably it will be the question that, what happens if the order against the requested order id does not exist.

To find the answer you will either:

  • Check out the documentation on the interface (What if documentation is missing?), or
  • Look into the source code (What if the source code is not accessible?)

In any of the cases, the highest probability is that the interface will return a null Order.

The client logic that you will write against such an interface will be something like:

Order order = orderService.findOrderWithId(orderId);
if (order == null) throw new OrderNotFoundException();
order.getTotalAmount();
... // do further processing

Most of the times the life of a developer is not easy. There are deadlines that you have to meet and under delivery pressure you may forget to add that null check if (order == null). And then you may see the dreaded NullPointerException when:

  • you run your unit tests (give a pat on your back for writing unit tests), or
  • your code is being tested by a QA (thank the QA for catching the bug in code), or
  • in the worst case when your code is running in production.

Representing the possibility of absence of a value using Optional

This is where Optional comes to rescue. It has been part of the Google’s Guava libraries for long. It has also recently been included in Java 8. Let’s try to re-write the interface using the Java 8’s version of Optional.

public interface OrderService {
  Optional<Order> findOrderWithId(long id);
}

Now if you use this interface to write some logic, you will observe that the return type itself is telling you that it may or may not be present. The code itself has become the documentation. As the return type is itself screaming out to you about its presence/absence, your chances of forgetting to handle the case of invalid order id becomes lesser.

Let’s see how a client code against such an interface will look like. The below code is intentionally made verbose for the purpose of illustration. A more concise version will also follow.

Optional<Order> orderOptional = orderService.findOrderWithId(orderId);
if (!orderOptional.isPresent()) throw new OrderNotFoundException();
Order order = orderOptional.get(); 
order.getTotalAmount();
... // do further processing

Note that the client code against interface to call an get() on the Optional to get the object which also forces the writer of client code to think about and handle the cases then value is not present. You can use isPresent() to check the presence or absence of a value.

A more concise version of the above code will be.

Order order = orderService.findOrderWithId(orderId)
    .orElseThrow(OrderNotFoundException::new);
order.getTotalAmount();
... // do further processing

There are multiple factory methods like Optional.of(T value), Optional.ofNullable(T value) and Optional.empty() which can used on the producer side. On the consumer side instance methods like get(), orElse(T other), orElseGet(Supplier<? extends T> other) and orElseThrow(Supplier<? extends X> exceptionSupplier) can be used.

Summary

Use Optional to represent the absence or presence of a value. Use it to specify return types when designing interfaces or APIs. Its usage gives the following advantages:

  • The code becomes the documentation for any client using the code.
  • The client code becomes less error-prone and can avoid NullPointerExceptions due to negligence.