Java functional programming of six: Optional

  • 2020-04-01 03:30:58
  • OfStack

Select a single element

It's intuitively easier to pick a single element than multiple elements, but there are some problems here. Let's take a look at what the general problem is, and then see how we can solve it with lambda expressions.

Let's create a new method to find an element that starts with a specific letter, and then print it out.


public static void pickName(
final List<String> names, final String startingLetter) {
String foundName = null;
for(String name : names) {
if(name.startsWith(startingLetter)) {
foundName = name;
break;
}
}

This method is as smelly as the old garbage truck. We create a new variable called foundName and initialize it to null -- that's where the stench comes from. We have to check if it is null, otherwise we will throw a NullPointerException or an error response. We also used an external iterator to loop through the list, and then had to jump out of the loop if we found the elements we wanted, which added to the old stink: basic type paranoia, imperative style, variability, all alive. Once out of the loop, we have to check the results before we can print. So much code for so few tasks.

Let's reanalyze the problem. We just want to be able to pick the first element that matches and safely handle the case where there is no such element. Let's rewrite the pickName method with a lambda expression.


public static void pickName(
final List<String> names, final String startingLetter) {
final Optional<String> foundName =
names.stream()
.filter(name ->name.startsWith(startingLetter))
.findFirst();
System.out.println(String.format("A name starting with %s: %s",
startingLetter, foundName.orElse("No name found")));
}

There are some powerful features in the JDK that make this code even more concise. First we used the filter method to get all the elements that met the criteria, and then we used the findFirst method of the Stream class to select the first element of the returned collection. This method returns an Optional object, which is the official deodorant for null variables in Java.

The Optional class is useful because you don't care if the result exists. It protects us from null pointer exceptions and makes it clear that no result is a possible result. With the isPresent() method, we can know whether the result exists or not. If we want to get the result value, we can use the get() method. We can also set it to a default value using the (this method name will shock you) orElse method, just like in the previous code.

Let's verify our pickName method with the set of friends we've been using.


pickName(friends, "N");
pickName(friends, "Z");

This code selects the first element that matches, and if it is not found, prints out a friendly message.

A name starting with N: Nate
A name starting with Z: No name found

The combination of the findFirst() method and the Optinal class reduced the amount of code we had, and it looked and felt good. But Optional does more than that. For example, in addition to providing a default value when the object doesn't exist, you can use it to run a piece of code or a lambda expression if the result does exist, like this:

foundName.ifPresent(name -> System.out.println("Hello " + name));

The elegant, functional style of streaming looks better than imperative code that selects the first name to match. But isn't this version of the call stream doing a little too much? Of course not, these methods are smart enough to work on demand (we'll get into this in the lazy evaluation of the Stream on page 113).

The example of picking a single element shows more of the power of the JDK library, so let's take a look at how lambda expressions can figure out a desired value based on a collection.


Related articles: