Java 11 Cookbook
上QQ阅读APP看书,第一时间看更新

How to do it...

  1. Create an Optional object using any of the methods that have been demonstrated, as follows:
Optional<Integer> prize1 = Optional.empty();
System.out.println(prize1.isPresent()); //prints: false
System.out.println(prize1); //prints: Optional.empty

Optional<Integer> prize2 = Optional.of(1000000);
System.out.println(prize2.isPresent()); //prints: true
System.out.println(prize2); //prints: Optional[1000000]

//Optional<Integer> prize = Optional.of(null);
//NullPointerException

Optional<Integer> prize3 = Optional.ofNullable(null);
System.out.println(prize3.isPresent()); //prints: false
System.out.println(prize3); //prints: Optional.empty

Notice that a null value can be wrapped inside an Optional object by using the ofNullable() method.

  1. It is possible to compare two Optional objects by using the equals() method, which compares them by value:
Optional<Integer> prize1 = Optional.empty();
System.out.println(prize1.equals(prize1)); //prints: true

Optional<Integer> prize2 = Optional.of(1000000);
System.out.println(prize1.equals(prize2)); //prints: false

Optional<Integer> prize3 = Optional.ofNullable(null);
System.out.println(prize1.equals(prize3)); //prints: true

Optional<Integer> prize4 = Optional.of(1000000);
System.out.println(prize2.equals(prize4)); //prints: true
System.out.println(prize2 == prize4); //prints: false

Optional<Integer> prize5 = Optional.of(10);
System.out.println(prize2.equals(prize5)); //prints: false

Optional<String> congrats1 = Optional.empty();
System.out.println(prize1.equals(congrats1));//prints: true

Optional<String> congrats2 = Optional.of("Happy for you!");
System.out.println(prize1.equals(congrats2));//prints: false

Please note that an empty Optional object is equal to an object that wraps the null value (the prize1 and prize3 objects in the preceding code). The prize2 and prize4 objects in the preceding code are equal because they wrap the same value, although they are different objects and the references do not match (prize2 != prize4). Also, notice that empty objects that wrap different types are equal (prize1.equals(congrats1)), which means that the equals() method of the Optional class does not compare the value type.

3. Use the or(Suppier<Optional<T>> supplier) method of the Optional class to reliably return a non-null value from the Optional object. If the object is empty and contains null, it returns another value contained in the Optional object that was produced by the provided Supplier function.

For example, if the Optional<Integer> lotteryPrize object can contain a null value, the following construct will return zero every time the null value is encountered:

       int prize = lotteryPrize.or(() -> Optional.of(0)).get();
  1. Use the ifPresent(Consumer<T> consumer) method to ignore the null value and to process the non-null value using the provided Consumer<T> function. For example, here is the processIfPresent(Optional<Integer>) method, which processes the Optional<Integer> lotteryPrize object:
void processIfPresent(Optional<Integer> lotteryPrize){
lotteryPrize.ifPresent(prize -> {
if(prize <= 0){
System.out.println("We've lost again...");
} else {
System.out.println("We've won! Your half is " +
Math.round(((double)prize)/2) + "!");
}
});

We can simplify the preceding code by creating the checkResultAndShare(int prize) method:

void checkResultAndShare(int prize){
if(prize <= 0){
System.out.println("We've lost again...");
} else {
System.out.println("We've won! Your half is " +
Math.round(((double)prize)/2) + "!");
}
}

Now, the processIfPresent() method looks much simpler: 

void processIfPresent(Optional<Integer> lotteryPrize){
lotteryPrize.ifPresent(prize -> checkResultAndShare(prize));
}
  1. If you do not want to ignore the null value and process it as well, you can use the ifPresentOrElse(Consumer<T> consumer, Runnable processEmpty) method to apply the Consumer<T> function to a non-null value, and use the Runnable functional interface to process the null value: 
void processIfPresentOrElse(Optional<Integer> lotteryPrize){
Consumer<Integer> weWon =
prize -> checkResultAndShare(prize);
Runnable weLost =
() -> System.out.println("We've lost again...");
lotteryPrize.ifPresentOrElse(weWon, weLost);
}

As you can see, we have reused the checkResultAndShare(int prize) method we just created.

  1. Using the orElseGet(Supplier<T> supplier) method allows us to replace an empty or null value (contained in the  Optional object) with the value produced by the provided Supplier<T> function:
void processOrGet(Optional<Integer> lotteryPrize){
int prize = lotteryPrize.orElseGet(() -> 42);
lotteryPrize.ifPresentOrElse(p -> checkResultAndShare(p),
() -> System.out.println("Better " + prize
+ " than nothing..."));
}
  1. Use the orElseThrow() method if you need to throw an exception in case an Optional object is empty or contains a null value: 
void processOrThrow(Optional<Integer> lotteryPrize){
int prize = lotteryPrize.orElseThrow();
checkResultAndShare(prize);
}

An overloaded version of the orElseThrow() method allows us to specify an exception and the message you would like to throw when the value contained in the Optional object is null

void processOrThrow(Optional<Integer> lotteryPrize){
int prize = lotteryPrize.orElseThrow(() ->
new RuntimeException("We've lost again..."));
checkResultAndShare(prize);
}
  1. Use the filter(), map(), and flatMap() methods to process Optional objects in a stream: 
void useFilter(List<Optional<Integer>> list){
list.stream().filter(opt -> opt.isPresent())
.forEach(opt -> checkResultAndShare(opt.get()));
}
void useMap(List<Optional<Integer>> list){
list.stream().map(opt -> opt.or(() -> Optional.of(0)))
.forEach(opt -> checkResultAndShare(opt.get()));
}
void useFlatMap(List<Optional<Integer>> list){
list.stream().flatMap(opt ->
List.of(opt.or(()->Optional.of(0))).stream())
.forEach(opt -> checkResultAndShare(opt.get()));
}

In the preceding code, the useFilter() method processes only those stream elements that have non-null values. The useMap() method processes all stream elements but replaces Optional objects without any value or by wrapping the null value with an Optional object that wraps zero. The last method uses flatMap(), which requires returning a stream from the provided function. Our example is quite useless in this respect because the function we pass as a flatMap() parameter produces a stream of one object, so using map() (as in the previous useMap() method) is a better solution here. We only did this for demonstrating how the flatMap() method can be plugged into the stream pipeline.