Java 9 | Excerpt

Java 9 Core Library Updates: Optionals and CompletableFutures

Changes to Optional and CompletableFuture help you better model error cases for business applications.

By Raoul-Gabriel Urma and Richard Warburton


Raoul-Gabriel Urma

Raoul-Gabriel Urma

Richard Warburton

Richard Warburton

In a previous article on Java 9’s changes, “Java 9 Core Library Updates: Collections and Streams,” we showed that there were lots of goodies that make developers more productive on a day-to-day basis. Java 9 isn’t just about big-picture improvements, such as modules and the Java 9 read-eval-print loop (REPL). In this article, we complete the examination of major changes to core libraries by looking at improvements to the Optional and CompletableFuture APIs.

Optional

Optional, a feature introduced in Java 8 to facilitate work with streams, was updated in Java 9. This release introduced the features discussed here: stream(), ifPresentOrElse(), and or().

stream(). If you’ve been using Java 8’s Stream API in conjunction with the Optional class, you might have encountered a situation in which you wanted to replace a stream of Optionals with values. For example, suppose you have a collection of settings that might have been set by a user. You’ve implemented the following lookupSettingByName() method, which returns an Optional<Setting> 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, you combined two Optional methods to achieve your goal. You used a filter on the isPresent() method in order to remove empty Optionals. Then you unboxed the Optional objects that you knew had a value with the get() method call.

You can make this code less clunky in Java 9. A method on Optional has been added that returns a stream called, funnily enough, stream(). It will give you a Stream with an element in it if the Optional has one, or it will be empty otherwise. Let’s see how the code looks 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 for performing one action if an Optional value is present or performing a different action if it’s absent. To understand this feature, let’s take an example of someone trying to check in for an airline flight to see how you would write this code first using null checks and then with the Optional type.

The user provides a booking reference, with which you look up their booking. If you have a booking associated with that reference, you display the check-in page; otherwise, you display a page explaining that the booking record is missing.

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

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

Now in Java 8, you could have refactored lookupBooking to return an Optional value, which would give you an explicit indication whether the booking can be found. The Optional would also help you think about the distinction between the booking being present and absent. This approach is better than checking for null because it forces you to explicitly think in terms of the domain model. The simplest refactoring for this code would have been to the following:

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

This pattern of taking an Optional and calling isPresent() and get() isn’t a particularly idiomatic use, because it makes the code verbose. In fact, it basically leaves you with code that is similar to the null-check version. Ideally, you want to be able to call a method on the Optional that is appropriate for your 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, this doesn’t meet your needs here, because it won’t handle the case in which the value is absent and you want to display the “missing booking” page. This is the use case that Java 9’s Optional improvements address. You could refactor your 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. It takes as an argument a function that creates an Optional. If the object on which it is invoked has a value present, the value is returned; otherwise, the function is called and its result is returned.

This is particularly useful when you have several methods that all return Optionals and you want to return the first one that is present. Suppose you want to look up information about clients using a company identifier, such as the company number. First, you want to check your existing client datastore to see whether the company is in there. If it isn’t, you want to create a new client by looking up information about the company in an alternative service.

Perhaps the provided ID is a typo from a user and the ID can’t be looked up at all. If you suppose that your methods just return null in order to indicate that the value is missing, you might write the following code:

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

If you refactor findClient() to return an Optional, you can use the orElseGet() method from Java 8, which will call your lookupCompanyDetails() method only if the Optional is absent, for example:

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

However, this still does not model your use case correctly. Because the companyId might not correspond to an actual company identifier, the lookupCompanyDetails() method can still fail. If you take the route of modeling failure using the Optional type, you should also make that method return an Optional.

This is where the new or() method comes into play. You get an Optional<Client> back. If you could look up the client in your database, then that client would be the value that is present. If it is a valid new company, that will be returned. If it contains a typo, the Optional will be empty. Here’s the code:

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

So far, this article has discussed how Optional in Java 8 has been improved in Java 9 with a series of targeted methods that satisfy missing use cases. The primitive, specialized Optional classes—such as OptionalInt—aren’t getting all the same love: stream() and ifPresentOrElse() were added to them, but or() was not.

CompletableFuture

Java 8 introduced CompletableFuture<T> as an enhancement to Future<T>. It is a class that lets you express the flow of information from different tasks using a callback-driven style. In the rest of this article, you will see the improvements that Java 9 brings to these Futures. We’ll first introduce a typical problem. We’ll then show several ways to address the problem in Java 8, and then we’ll demonstrate in the final section how Java 9 provides better alternatives.



Raoul-Gabriel Urma (@raoulUK) is the CEO and cofounder of Cambridge Spark, a leading learning community for data scientists and developers in the UK. He is also chairman and cofounder of Cambridge Coding Academy, a community of young coders and students. Urma is coauthor of the best-selling programming book Java 8 in Action (Manning Publications, 2015). He holds a PhD in computer science from the University of Cambridge.

Richard Warburton (@richardwarburto) is a software engineer, teacher, author, and Java Champion. He is the author of the best-selling Java 8 Lambdas (O’Reilly Media, 2014) and helps developers learn via Iteratr Learning and at Pluralsight. Warburton has delivered hundreds of talks and training courses. He holds a PhD from the University of Warwick.

NOTE: This article is excerpted from Java Magazine September/October 2017. Continue to the full article.