1. Introduction
Null references and values have tormented programmers for years. Tony Hoare, the creator of null references, famously called his invention a “billion-dollar mistake.” Java, in particular, has had a long struggle with null values and the dreaded NullPointerException.
Java 8 introduced the Optional class to address this challenge and ensure proper handling of null and empty results. In this tutorial, we’ll explore performing actions only when all Optional variables contain values, ignoring the action otherwise.
2. Assumptions
This tutorial demonstrates using three Optional variables, but the concepts can be easily extended to handle more. Additionally, let’s declare these variables, which we’ll use throughout this article:
var name = Optional.of("Jean-Luc Picard");
var designation = Optional.of("Captain");
var ship = Optional.of("USS Enterprise D");
In this instance, for the sake of simplicity, we defined Optional<String>. Nevertheless, the principles discussed in the article apply equally to other reference types.
3. Using isPresent()
Optional offers a useful method, isPresent(), to determine if a value is contained within it. This method returns true if a value exists and false if the Optional is empty. Let’s look at the implementation:
var action = false;
if (name.isPresent() && designation.isPresent() && ship.isPresent()) {
action = true;
}
Assertions.assertTrue(action);
In this example, we create the Optional instances using Optional.of(). Subsequently, we use the isPresent() on each instance and act only if all the values are present. While this approach is notably simple and readable, it becomes cumbersome when dealing with multiple variables.
4. Using flatMap() and map()
Java 8 brought functional programming concepts into the language. With methods like flatMap() and map(), we can now perform actions on values contained in container types, such as Optional. Utilizing flatMap() and map(), we can effectively check and execute actions only when all values are present.
Let’s look at the implementation using this approach:
var action = name.flatMap(n -> designation.flatMap(d -> ship.map(s -> true)));
Assertions.assertEquals(action, Optional.of(true));
In the above example, we chained various Optional instances using flatMap() and map(). These functions are designed to short-circuit; if a value doesn’t exist at any step in the chain, the operation is immediately terminated, and an empty result is returned.
We can verify this behavior by introducing another test case:
var name = Optional.of("Jean-Luc Picard");
var designation = Optional.of("Captain");
var ship = Optional.empty();
var action = name.flatMap(n -> designation.flatMap(d -> ship.map(s -> true)));
Assertions.assertTrue(action.isEmpty());
Here, we can see that the action variable is empty because the ship does not have a value.
While this approach is powerful, it tends to become more verbose when numerous values must be chained together.
5. Using ifPresent()
Alternatively, when we don’t require a return value and only aim to execute an action if all values exist, we can utilize ifPresent() with a lambda expression. Let’s look at the sample code:
name.ifPresent(n -> designation.ifPresent(d -> ship.ifPresent(s -> System.out.println("Perform action instead!"))));
In this scenario, we chain each Optional instance using ifPresent() and execute the action only if all the values exist.
6. Using Stream.of()
Java Streams offers another approach for ensuring actions only happen when all values are present. We can create a stream of Optional values using Stream.of() and utilize the allMatch() method to check if every element within the stream contains a value.
Let’s look at the implementation:
var status = false;
var allPresent = Stream.of(name, designation, ship).allMatch(Optional::isPresent);
if (allPresent) {
// Perform action if all values present
status = true;
}
Assertions.assertTrue(status);
In this example, we use allMatch() alongside Optional.isPresent() to verify that all elements within the stream are present.
This provides a concise way to perform the required validation. Unlike the other approaches, adding more optional values doesn’t reduce the readability. This makes Streams a highly scalable solution for handling a growing number of optional values.
7. Conclusion
In this tutorial, we explored different approaches to perform an action only if all the optional values are available. We initially examined the straightforward if…else condition and then explored alternative techniques utilizing functional programming concepts and the Streams API. Ultimately, the most suitable approach depends on the specific context of the situation at hand.
As always, the sample code used in this article is available over on GitHub.