Optional type was introduced in Java 8 for helping developers deal with nullable values properly.
Java 9 has introduced few additional methods in the Optional API that enhances its capabilities even further.
In this article, I’ll take you through these new methods that are introduced in Java 9, and provide simple examples to demonstrate their usage.
If you are new to Optional and want to read the basics first, then check out my Java 8 Optional tutorial.
In this article, I’ll use JShell for running all the code samples. JShell is also a new Java 9 feature. You can learn more about it from my Introduction to Java 9 JShell.
All right! Let’s now take a look at all the new methods added in the Optional API one by one.
Java 9 Optional.or() method
Optional already has two methods namely orElse()
and orElseGet()
to return a default value when an Optional is empty.
Consider the following empty Optional for example -
jshell> Optional<String> greeting = Optional.empty()
greeting ==> Optional.empty
You can use orElse()
method with the above Optional to directly return a default value like this -
jshell> String greetingText = greeting.orElse("Hello, World");
greetingText ==> "Hello, World"
Or, You can use orElseGet()
method to provide a Supplier function, which returns the default value -
jshell> String greetingText = greeting.orElseGet(() -> {
...> Random rand = new Random();
...> return "Hello CalliCoder Blog Readers - " + rand.nextInt(10);
...> })
greetingText ==> "Hello CalliCoder Blog Readers - 7"
Both of these methods return a default value when the Optional is empty. If the Optional wasn’t empty, these methods would return the value inside the Optional instead of the default value. In any case, the final returned value is an unwrapped type instead of an Optional.
But, there might be situations when you would want the final returned value to be an Optional instead of a simple type.
The or()
method is provided for these situations. It is similar to orElseGet()
method except that the Supplier function it takes, returns an Optional instead of a simple type -
jshell> Optional<String> optionalGreetingText = greeting.or(() -> {
...> String text = "Nullable Greeting";
...> return Optional.ofNullable(text);
...> });
optionalGreetingText ==> Optional[Nullable Greeting]
The final result, as you can see, is an Optional in this case instead of a simple type. The or()
method returns the original Optional if a value is present, otherwise, returns the Optional produced by the Supplier function.
Java 9 Optional.ifPresentOrElse() method
How would you write an If-Present-Else logic with Java 8 Optional?
You could use the isPresent()
method to check if a value is present or not, and do something like this -
jshell> Optional<String> name = Optional.ofNullable("Rajeev");
name ==> Optional[Rajeev]
jshell> if(name.isPresent()) {
...> System.out.println("Hello " + name.get());
...> } else {
...> System.out.println("Hello Guest");
...> }
Hello Rajeev
This is OK but too verbose. We also have an ifPresent()
method that accepts a Consumer function which is invoked when a value is present inside the Optional -
jshell> name.ifPresent(value -> System.out.println("Hello " + value));
Hello Rajeev
The ifPresent()
method is convenient and less verbose because we can pass the Consumer function in the form of a lambda expression. But unfortunately, we can’t write the Else
logic when we use ifPresent()
.
That is exactly what ifPresentOrElse()
method is for -
jshell> name.ifPresentOrElse(
...> value -> System.out.println("Hello " + value),
...> () -> System.out.println("Hello Guest")
...> )
Hello Rajeev
The ifPresentOrElse()
method accepts two parameters - a Consumer and a Runnable. We can pass both as lambda expressions to the method.
The Consumer is invoked if a value is present inside the Optional, and the Runnable is invoked if the Optional is empty.
Java 9 Optional.stream() method
The new stream()
method allows you to convert an Optional to a Stream.
If the Optional contains a value, it will return a Stream containing only that value, otherwise, it will return an empty Stream -
jshell> Optional<String> myOptional = Optional.of("foo")
myOptional ==> Optional[foo]
jshell> Stream<String> myOptionalStream = myOptional.stream()
myOptionalStream ==> java.util.stream.ReferencePipeline$Head@65e2dbf3
jshell> myOptionalStream.forEach(System.out::println)
foo
The stream()
method will be very helpful when you’re dealing with a collection of Optional elements. It will automatically filter out non-empty Optionals and allow you to write transformations on the Optionals seamlessly.
Here is how you would convert a list of Optional elements to a Stream of values using Java 9’s stream()
method-
jshell> List<Optional<String>> animalOptionals = List.of(Optional.of("Elephant"), Optional.empty(), Optional.of("Lion"), Optional.of("Tiger"), Optional.empty())
animalOptionals ==> [Optional[Elephant], Optional.empty, Optional[Lio ... al[Tiger], Optional.empty]
jshell> Stream<String> animals = animalOptionals.stream().flatMap(Optional::stream)
animals ==> java.util.stream.ReferencePipeline$7@551aa95a
jshell> animals.forEach(System.out::println)
Elephant
Lion
Tiger
Since, Optional.stream() method returns an empty stream if the Optional is empty, the above List-of-Optionals to Stream-of-Values conversion will automatically filter out non-empty Optional elements.
Conclusion
These new improvements in the Optional API will surely help Java developers write better code with Optionals.
I really like the addition of ifPresentOrElse()
method. I almost every time encounter situations when I need to write an If-Present-Else logic in my code, and I’m forced to write if-else
code with isPresent()
. But now I can use this new method to pass two lambda expressions for both the cases.
Let me know what do you think about these new methods in the comment section below.
Thanks for reading. See you in the next post. Happy Coding :)