Optional Improvements in Java 9

Written on September 05, 2016

So we’ve already talked a little bit about the improvements in Java 9 to two features that were introduced in Java 8: Streams and Collectors. But what about one of our other Java 8 friends: Optional. At Iteratrlearning we’re real fans of the Optional type. We’ve found that, if used well, it can make your code more explicit and reduce the scope for bugs.

Of course nothing is perfect. Optional in Java 8 was missing a few features that have been improved upon in Java 9 and that’s what we’ll be talking about here: stream(), ifPresentOrElse() and or().

stream()

If you’ve been using Java 8’s Stream API in conjunction with the Optional class then you might have hit a situation where you want to replace a stream of Optionals with values that are present. For example, let’s suppose you’ve got a set of settings that may be set by a user. You’ve implemented a lookupSettingByName() method that returns an Optional if the configuration setting has been set by the user.

List<Setting> settings =
    SETTING_NAMES.stream()
                 .map(this::lookupSettingByName)
                 .filter(Optional::isPresent)
                 .map(Optional::get)
                 .collect(toList());

In this example we’re combining two optional methods to achieve our goal. We’ve used a filter on the isPresent() method in order to remove empty Optionals. We then unboxed the optionals that we knew had a value in with the get() method call.

This is a functional solution, but we can streamline things in Java 9. Here a method on Optional has been added that returns a Stream called, funnily enough, stream(). That will give us a stream with an element in if the Optional has one, or empty otherwise. Let’s see how our code would look with this approach:

List<Setting> settings =
    SETTING_NAMES.stream()
                 .map(this::lookupSettingByName)
                 .flatMap(Optional::stream)
                 .collect(toList());

This new addition also means that it is simpler to integrate Optional with APIs expecting to work with Streams.

ifPresentOrElse()

The new ifPresentOrElse method encodes a common pattern where you want to perform an action if an Optional value is present, or a different action if it’s absent. In order to understand it better we’ll take an example of someone trying to checkin to an airline flight and see how you would write this code both using null checks and also the Optional type.

Our user provides us with a booking reference, which we lookup their booking with. If we have a booking associated with that reference then we can display the check-in page to them, otherwise we’ll just display a page explaining that their booking record is missing.

If our lookupBooking method were to return null in order to indicate that the booking is missing then our code might look like the following:

Booking booking = lookupBooking(bookingReference);
if (booking != null) {
    displayCheckIn(booking);
} else {
    displayMissingBookingPage();
}

Now in Java 8 we could have refactored lookupBooking to return an Optional value, which gave us an explicit indication that a booking may not be looked up. It also helps the user to think about the distinction between the booking being present and absent, rather than simply hoping that they have a null check. The simplest refactor for this code would have been to the following:

Optional<Booking> booking = lookupBooking(bookingReference);
if (booking.isPresent()) {
    displayCheckIn(booking.get());
} else {
    displayMissingBookingPage();
}

Now this pattern of taking an Optional and calling isPresent() and get() isn’t a particularly idiomatic use. In fact it basically leaves us with similar code to the null checked version. Ideally we want to be able to call a method on the Optional that is appropriate for our use case. Effectively moving to a tell-don’t-ask style of coding. Optional from Java 8 has an ifPresent method that will invoke its callback if the value inside the Optional is present, for example:

lookupBooking(bookingReference)
    .ifPresent(
        this::displayCheckIn);

Unfortunately it doesn’t meet our needs here because it won’t handle the case where the value is absent and we want to display our missing booking page. This is the use case that Java 9’s Optional addresses. We could refactor our original code as follows:

lookupBooking(bookingReference)
    .ifPresentOrElse(
        this::displayCheckIn,
        this::displayMissingBookingPage);

or()

Another method that has been added to Optional in Java 9 is the succinctly named or() method. This method takes a function that creates an Optional as an argument. If the object that it gets invoked upon has a value present then it is returned, otherwise the function is invoked and its result returned.

This is particularly useful when you have a couple of methods that all return optionals and you want to return the first one that is present. Let’s suppose that we want to lookup information about client’s using a company identifier, such as their company number. Firstly we want to check our existing client datastore and see if the company is in there. If it isn’t we want to create a new client by looking up the information about the company from Companies House. Now it might be the case that the provided id is a typo from a user and couldn’t be looked up at all. If we suppose that our methods just return null in order to indicate that the value is missing then we might write the following code:

Client client = findClient(companyId);
if (client == null) {
    client = lookupCompanyDetails(companyId);
}
// client could still be null

Now if someone refactors findClient() to return an Optional, we can use the orElseGet() method from Java 8 which will only call our lookupCompanyDetails() method if the Optional is absent. For example:

Client client = 
    findClient(companyId)
    .orElseGet(() -> lookupCompanyDetails(companyId));

Unfortunately this still doesn’t model our use case correctly. Since the companyId may not correspond to an actual company identifier the lookupCompanyDetails() method can still fail. If we’re going down the route of modelling failure using the Optional type then we should also make that method return an Optional.

This is where the new or() method comes into play. We get an Optional<Client> back. If we could lookup the client in our database then that would be the value present, if it was a valid new company then that would be returned, if it was a typo then the Optional box would be empty. Et voila:

Optional<Client> client = 
    findClient(companyId)
    .or(() -> lookupCompanyDetails(companyId))

get()

There is an outstanding proposal to deprecate Optional.get() and rename it to something else. The full details of this proposal can be read here. Despite this proposal having merit the schedule constraints mean that it is unlikely to make Java 9, but might happen in Java 10.

Conclusions

We’ve talked about Java 8’s Optional class is being improved in Java 9 with a series of targeted method that each fill a missing use case. Unfortunately, the primitive specialised Optional classes, such as OptionalInt aren’t getting all the same love - they have had stream() and ifPresentOrElse() added, but not or(). They have always been missing some of the methods from Optional and are increasingly looking like poor cousins.

If you are interested in an intensive Java course for your team, check out our Java Software Development Bootcamp or Modern Development with Java.