Some operations specific to lambda expressions return Optional (for example, findFirst(), findAny(), reduce(), and so on). Trying to address these Optional classes via the isPresent()-get() pair is a cumbersome solution because we have to break the chain of lambdas, add some conditional code via the if-else blocks, and consider resuming the chain.
The following snippet of code shows this practice:
private static final String NOT_FOUND = "NOT FOUND";
List<Book> books...;
...
// Avoid
public String findFirstCheaperBook(int price) {
Optional<Book> book = books.stream()
.filter(b -> b.getPrice()<price)
.findFirst();
if (book.isPresent()) {
return book.get().getName();
} else {
return NOT_FOUND;
}
}
One step further and we may have something like the following:
// Avoid
public String findFirstCheaperBook(int price) {
Optional<Book> book = books.stream()
.filter(b -> b.getPrice()<price)
.findFirst();
return book.map(Book::getName)
.orElse(NOT_FOUND);
}
Using orElse() instead of the isPresent()-get() pair is better. But it will be even better if we use orElse() (and orElseFoo()) directly in the chain of lambdas and avoid disrupted code:
private static final String NOT_FOUND = "NOT FOUND";
...
// Prefer
public String findFirstCheaperBook(int price) {
return books.stream()
.filter(b -> b.getPrice()<price)
.findFirst()
.map(Book::getName)
.orElse(NOT_FOUND);
}
Let's have one more problem.
This time, we have an author of several books, and we want to check whether a certain book was written by this author. If our author didn't write the given book, then we want to throw NoSuchElementException.
A really bad solution to this will be as follows:
// Avoid
public void validateAuthorOfBook(Book book) {
if (!author.isPresent() ||
!author.get().getBooks().contains(book)) {
throw new NoSuchElementException();
}
}
On the other hand, using orElseThrow() can solve the problem very elegantly:
// Prefer
public void validateAuthorOfBook(Book book) {
author.filter(a -> a.getBooks().contains(book))
.orElseThrow();
}