Before we continue and discuss the next group of terminal operations findAny(), findFirst(), max(), and min(), we need to understand the role and the functionality of an object of the java.util.Optional class.
The purpose of an object of Optional class is to avoid returning null as it may cause NullPointerException. Instead, an Optional object provides methods that allow checking the presence of a value and substituting it with a predefined value if the return value is null.
Factory methods of(), empty(), ofNullable()
First, let us look at the factory methods of the class Optional:
static <T> Optional<T> of(T value). Creates an Optional object that describes (contains) the given non-null value.
static <T> Optional<T> empty(). Creates an empty Optional object that does not describe (contain) any value.
static <T> Optional<T> ofNullable(T value). Creates an Optional object that describes (contains) the given value, if non-null, otherwise returns an empty Optional (same as if Optional.empty() is called).
The following are examples of their usage:
//Optional that describes (contains) a String value "Abc":
Optional<String> opt = Optional.of("Abc");
//The following throws NullPointerException:
Optional<String> opt = Optional.of(null);
//Optional without any value of any type:
Optional<String> opt = Optional.empty();
//Optional that describes (contains) a String value "Abc"
// (same as the Optional.of("Abc")):
Optional<String> opt = Optional.ofNullable("Abc");
//Optional without any value of any type
// (same as the Optional.empty()):
Optional<String> opt = Optional.ofNullable(null);
Now let us look at the other methods of the class Optional.
Method get()
The get() method retrieves value (if it is present) from the Optional object:
Optional<String> opt1 = Optional.of("1");
System.out.println(opt1.get()); //prints: 1
Optional<String> opt3 = Optional.ofNullable("1");
System.out.println(opt3.get()); //prints: 1
Optional<String> opt4 = Optional.ofNullable(null);
System.out.println(opt4.get()); //NoSuchElementException
Optional<String> opt5 = Optional.empty();
System.out.println(opt5.get()); //NoSuchElementException
As you can see, in two out of four cases above, the get() method throws an exception. There are two ways to avoid it:
— by using method isEmpty() or isPresent() first,
— by using Optional operations or(), orElse(), orElseGet(), ifPresent(), ifPresentOrElse().
You can also use the orElseThrow() operation to throw a specific exception type.
Methods isEmpty() and isPresent()
These two methods are defined as follows:
boolean isEmpty(). Returns true if a value is not present, otherwise false.
boolean isPresent(). Returns false if a value is not present, otherwise true.
The following code demonstrates how they work:
Optional<String> opt1 = Optional.of("1");
System.out.print(opt1.get()); //prints: 1
System.out.print(opt1.isEmpty()); //prints: false
System.out.print(opt1.isPresent()); //prints: true
Optional<String> opt2 = Optional.ofNullable(null);
System.out.print(opt2.get()); //NoSuchElementException
System.out.print(opt2.isEmpty()); //prints: true
System.out.print(opt2.isPresent()); //prints: false
Optional<String> opt3 = Optional.ofNullable("1");
System.out.print(opt3.get()); //prints: 1
System.out.print(opt3.isEmpty()); //prints: false
System.out.print(opt3.isPresent()); //prints: true
Optional<String> opt4 = Optional.empty();
System.out.print(opt2.get()); //NoSuchElementException
System.out.print(opt4.isEmpty()); //prints: true
System.out.print(opt4.isPresent()); //prints: false
So, before retrieving the value, you can call isEmpty() or isPresent() on an Optional object to avoid an exception while using get(). This functionality is necessary because we cannot check the value for null without retrieving it. if it feels too verbose, you can try and use the operations described in the next section.
Method or()
This operation is defined as follows:
Optional<T> or(Supplier<Optional<T>> supplier). Returns an Optional with the value if a value is present, otherwise returns an Optional produced by the specified function supplier.
The following code examples demonstrate its functionality:
Optional<String> opt1 = Optional.of("1")
.or(() -> Optional.of("42"));
System.out.print(opt1.get()); //prints: 1
System.out.print(opt1.isEmpty()); //prints: false
System.out.print(opt1.isPresent()); //prints: true
Optional<String> opt2 = Optional.of("1")
.filter(s -> s.equals("2"))
.or(() -> Optional.of("42"));
System.out.print(opt2.get()); //prints: 42
System.out.print(opt2.isEmpty()); //prints: false
System.out.print(opt2.isPresent()); //prints: true
Method orElse()
This operation is defined as follows:
T orElse(T other). Returns the value if a value is present, otherwise returns the specified value other.
The following code examples demonstrate its functionality:
String result1 = Optional.of("1")
.orElse("42");
System.out.print(result1); //prints: 1
String result2 = Optional.of("1")
.filter(s -> s.equals("2"))
.orElse("42");
System.out.print(result2); //prints: 42
Method orElseGet()
This operation is defined as follows:
T orElseGet(Supplier<T> supplier). Returns the value if a value is present, otherwise returns the result produced by the specified function supplier.
The following code examples demonstrate its functionality:
String result1 = Optional.of("1")
.orElseGet(() -> "42");
System.out.print(result1); //prints: 1
String result2 = Optional.of("1")
.filter(s -> s.equals("2"))
.orElseGet(() -> "42");
System.out.print(result2); //prints: 42
Supplier<String> doSomethingElse = () -> {
//Code that does something else
return "42";
};
String result3 = Optional.of("1")
.orElseGet(doSomethingElse);
System.out.print(result3); //prints: 1
String result4 = Optional.of("1")
.filter(s -> s.equals("2"))
.orElseGet(doSomethingElse);
System.out.print(result4); //prints: 42
Method orElseThrow()
This operation is defined as follows:
T orElseThrow(). Returns the value if a value is present, otherwise throws NoSuchElementException.
The following code examples demonstrate its functionality:
String result = Optional.of("1")
.orElseThrow();
System.out.println(result); //prints: 1
String result = Optional.of("1")
.filter(s -> s.equals("2"))
.orElseThrow(); //NoSuchElementException
Supplier<Exception> generateException =
() -> new RuntimeException("No value!");
String result = Optional.of("1")
.orElseThrow(generateException);
System.out.println(result); //prints: 1
String result = Optional.of("1")
.filter(s -> s.equals("2"))
.orElseThrow(generateException); //RuntimeException: No value!
Method ifPresent()
This operation is defined as follows:
void ifPresent(Consumer<T> action). Applies the specified action to the value if a value is present, otherwise does nothing.
The following code examples demonstrate its functionality:
Optional.of("1")
.ifPresent(System.out::print); //prints: 1
Optional.of("1")
.filter(s -> s.equals("2"))
.ifPresent(System.out::print); //prints nothing
Consumer<String> doSomething = s ->
{
//Code that does something
System.out.print(s);;
};
Optional.of("1")
.ifPresent(doSomething); //prints: 1
Optional.of("1")
.filter(s -> s.equals("2"))
.ifPresent(doSomething); //prints nothing
Method ifPresentOrElse()
This operation is defined as follows:
void ifPresentOrElse(Consumer<T> action, Runnable emptyAction). Applies the specified action to the value if a value is present, otherwise, it performs the specified action.
The following code examples demonstrate its functionality:
Optional.of("1")
.ifPresentOrElse(
System.out::print,
() -> System.out.print("42") //prints: 1
);
Optional.of("1")
.filter(s -> s.equals("2"))
.ifPresentOrElse(
System.out::print,
() -> System.out.print("42") //prints: 42
);
Consumer<String> doSomething = s ->
{
//Code that does something
System.out.print(s);;
};
Runnable doSomethingElse = () ->
{
//Code that does something else
System.out.println("42");;
};
Optional.of("1")
.ifPresentOrElse(doSomething,
doSomethingElse); //prints: 1
Optional.of("1")
.filter(s -> s.equals("2"))
.ifPresentOrElse(doSomething,
doSomethingElse); //prints: 42
Other Stream-compatible methods
There are also Optional methods familiar to you as Stream intermediate operations:
Optional filter(Predicate predicate). If a value is present, and the value matches the given predicate, returns an Optional describing the value, otherwise returns an empty Optional.
Optional map(Function mapper). If a value is present, returns an Optional describing (as if by ofNullable(T)) the result of applying the given mapping function to the value, otherwise returns an empty Optional.
Optional flatMap(Function> mapper). If a value is present, returns the result of applying the given Optional-bearing mapping function to the value, otherwise returns an empty Optional.
Stream stream(). If a value is present, it returns a sequential Stream containing only that value, otherwise it returns an empty Stream.
The following code demonstrates these operations:
String v1 = Optional.of("1")
.filter(s -> s.equals("1"))
.or(() -> Optional.of("42"))
.get();
System.out.print(v1); //prints: 1
String v2 = Optional.of("1")
.filter(s -> s.equals("2"))
.or(() -> Optional.of("42"))
.get();
System.out.print(v2); //prints: 42
int v3 = Optional.of("abc")
.map(s -> s.length())
.orElse(42);
System.out.print(v3); //prints: 3
int v4 = Optional.of(List.of("a","b","c"))
.map(List::size)
.orElse(42);
System.out.print(v4); //prints: 3
int v5 = Optional.of(Optional.of("abc"))
.map(o -> Optional.of(o.get().length()).orElse(23))
.orElse(42);
System.out.print(v5); //prints: 3
int v6 = Optional.of(Optional.of("abc"))
.flatMap(o -> Optional.of(o.get().length()))
.orElse(42);
System.out.print(v6); //prints: 3
Optional.of("abc")
.stream()
.forEach(System.out::print); //prints: abc
Optional.of("abc")
.filter(s -> s.equals("42"))
.stream()
.forEach(System.out::print); //prints:
In the next post, we will continue discussing terminal operations and present the following four:
-
Optional<T> findAny()-
Optional<T> findFirst()-
Optional<T> max(Comparator<T> comparator)-
Optional<T> min(Comparator<T> comparator)
See other posts on Java 8 streams and posts on other topics.
You can also use navigation pages for Java stream related blogs:
— Java 8 streams blog titles
— Create stream
— Stream operations
— Stream operation collect()
The source code of all the code examples is here in GitHub.
Send your comments using the link Contact or in response to my newsletter.
If you do not receive the newsletter, subscribe via link Subscribe under Contact.