Introduction
In the previous post, A complete guide to Java Optional, I explained about the Optional class and its use in Java. In this post, we will see the new methods added to the Optional class from Java 9 onwards (upto 11).
The new APIs summary
We will look at the following methods
- ifPresentOrElse()
- or()
- Stream()
- orElseThrow()
- isEmpty()
Execute an action if Optional is empty
There is a method in the Optional class, isPresent, that takes a Consumer as a parameter. If the Optional is not empty, then the value is passed to the consumer. Thus, we can print the value of an Optional like
optional.ifPresent(System.out::println)
But, there is no way to perform an action (like printing) when the optional is empty. There existed two options.
Old way 1 – Using isPresent
We can use an if condition to check if the optional is empty or not and take appropriate action.
Optional<String> o1 = Optional.of("value"); Optional<String> o2 = Optional.empty(); if (o1.isPresent()) { System.out.println("Found " + o1.get()); } else { System.out.println("Not found");
}
This prints Found value when using o1 and prints Not found when using o2.
Old way 2 – Using orElseGet
This approach is uglier than the previous option. We can use a map and orElseGet. The map just returns the same value after printing (or any action that we want to do). Using orElseGet block, we print that the optional is empty and simply return a null. As mentioned earlier, I do not recommend this approach as it is very misleading.
o1.map(s -> { System.out.println("Found " + s); return s; }).orElseGet(() -> { System.out.println("Not found"); return null; });
Java 9 added ifPresentOrElse method that would enable us in doing just this. It takes an additional Runnable parameter in addition to the Consumer that ifPresent takes.
o1.ifPresentOrElse(s -> System.out.println("Found " + s), () -> System.out.println("Not found"));
Return an alternative (or default) Optional
Optional has orElse and orElseGet methods to return a default value when the Optional is empty. But, what if we need to return a default Optional? We have to wrap it in an Optional ourselves. Java 9 added a or method that allows us to pass a supplier that returns an Optional. An example will make this clear
Optional<String> o1 = Optional.of("value"); Optional<String> o2 = Optional.empty(); o1 = o1.or(() -> Optional.of("default")); System.out.println(o1); //Optional[value] o2 = o2.or(() -> Optional.of("default")); System.out.println(o2); //Optional[default]
Create a Stream from an Optional
API: Stream<T> stream()
Added in: Java 9 – JDK-8050820
//Just a few fruits shown here private static final List<String> FRUITS = Arrays.asList("apple", "mango", "banana", "peach"); private static Optional<String> transform(String s) { return FRUITS.contains(s) ? Optional.of(s) : Optional.empty(); }
Continuing on with the example, we start with a list of strings and our goal is to filter (pick) only the fruits and convert them to uppercase. We can do it like
List<String> input = Arrays.asList("apple", "mango", "banana"); input.stream() .map(this::transform) .filter(Optional::isPresent) .map(Optional::get) .map(String::toUpperCase) .forEach(System.out::println);
It calls the transform method shown above to filter the input strings. Since, it returns an Optional, we have to again filter the ones that are not empty and map it (calling get on the Optional). Then, we convert it to uppercase to print it.
input.stream() .map(this::transform) .flatMap(Optional::stream) .map(String::toUpperCase) .forEach(System.out::println);
This simplifies the processing to a large extent and makes the code less verbose.
.filter(Optional::isPresent).map(Optional::get)
with .flatMap(Optional::stream)
.Optional get method throws NoSuchElementException
New API: T orElseThrow()
Added in: Java 10 – JDK-8140281
//Using o1 and o2 defined earlier System.out.println(o1.orElseThrow()); //value System.out.println(o2.orElseThrow()); //java.util.NoSuchElementException: No value present
Is the Optional empty?
New API: boolean isEmpty()
Added in: Java 11 – JDK-8184693
//Using o1 and o2 defined earlier System.out.println(o1.isEmpty()); //false System.out.println(o2.isEmpty()); //true
Optional flatMap signature change
Changed in: Java 9
The old signature (in Java 8) of flatMap method is
The new signature is
The difference is in the type of the value returned by the function. Before it was returning an Optional<U>; now, with this change, its type is ? extends Optional<? extends U>> .
Why is this significant? We can understand this with an example. Say, we have two classes Human and a Student. and the Student extends Human.
static class Human { private String name; public Human(String name) { this.name = name; } public String getName() { return name; } } static class Student extends Human { Student(String name) { super(name); } }
Let us say we have a method that maps a string to a Student. But, it returns an Optional<Student>.
private static Optional<Student> transformStringToStudent(String str) { //Assume some case returns Optional.empty() too return Optional.of(new Student(str)); }
We begin with an Optional<String> and want to map it to a Student. Since the mapping function returns an Optional in itself, using a map on the Optional would give us an Optional<Optional<Student>>. We can use a flatMap to flatten it.
Optional<String> input = Optional.of("some-name"); Optional<Student> studentResult = input.flatMap(this::transformStringToStudent; //do something with the result
This would work fine with both Java 8 and Java 9.
Optional<Human> result = input.flatMap(this::transformStringToStudent);
Looking closer:
Conclusion
We looked at the new methods or APIs added to Java optional class from Java 9 through 11. We also saw the API change involving the flatMap method in the Optional class.