Java 8 introduces a new way to think about asynchronous programming with a new class, CompletableFuture. It’s an improvement on the old Future class, with operations inspired by similar design choices made in the new Streams API (i.e., declarative flavor and ability to chain methods fluently). In other words, you can declaratively process and compose multiple asynchronous tasks.
Here’s an example that concurrently queries two blocking tasks: a price finder service along with an exchange rate calculator. Once the results from the two services are available, you can combine their results to calculate and print the price in GBP:
findBestPrice("iPhone6")
.thenCombine(lookupExchangeRate(Currency.GBP),
this::exchange)
.thenAccept(localAmount -> System.out.printf("It will cost you %f GBP\n", localAmount));
private CompletableFuture
Optional
Java 8 introduces a new class called Optional. Inspired by functional programming languages, it was introduced to allow better modeling in your codebase when a value may be present or absent. Think of it as a single-value container, in that it either contains a value or is empty. Optional has been available in alternative collections frameworks (like Guava), but is now available as part of the Java API. The other benefit of Optional is that it can protect you against NullPointerExceptions. In fact, Optional defines methods to force you to explicitly check the absence or presence of a value. Take the following code as an example: getEventWithId(10).getLocation().getCity();
If getEventWithId(10) returns null, then the code throws a NullPointerException. If getLocation() returns null, then it also throws a NullPointerException. In other words, if any of the methods return null, a NullPointerException could be thrown. You can avoid this by adopting defensive checks, like the following: public String getCityForEvent(int id) { Event event = getEventWithId(id); if(event != null) { Location location = event.getLocation(); if(location != null) { return location.getCity(); } } return "TBC"; }
In this code, an event may have an associated location. However, a location always has an associated city. Unfortunately, it’s often easy to forget to check for a null value. In addition, the code is now more verbose and harder to follow. Using Optional, you can refactor the code to be more concise and explicit, like so: public String getCityForEvent(int id) { Optional.ofNullable(getEventWithId(id)) .flatMap(this::getLocation) .map(this::getCity) .orElse("TBC"); }
At any point, if a method returns an empty Optional, you get the default value "TBC".
Chapter 2. Adopting Lambda Expressions
In this chapter, you’ll learn how to adopt
Why Lambda Expressions?