The Tale “Ivan The Programmer” Or “Optional in Java 9”

Image: https://botan.cc/

In a series of articles:

Java 8 Optional and objects with dynamic structure. Part 1,

Java 8 Optional and objects with dynamic structure. Part 2,

Java 8 Optional and objects with dynamic structure. Part 3,

Java 8 Optional and objects with dynamic structure. Part 4. Using Optional in transformers and consumers and

Compensate a lack in Java: The class Result

I described the applications and advantages of the Optional class that appeared in Java 8, as well as some flaws in implementing this class.

In Java 9, some shortcomings were eliminated by extending the class with new methods. We will consider them in this article.

The source texts of this all my articles on this subject you will find in GitHub: https://github.com/vsirotin/Smartenesse-Java

There are three new methods added to the Optional<T> class in Java 9: stream(), ifPresentsOrElse() and or().
Let us begin our consideration.
This will help us Ivan – a descendant of the famous hero of Russian folk tales. Java-progreammer Ivan works in the project in remote access mode (remote). This gave him the opportunity to move to a house in the village. In his garden he planted several beds of vegetables.

The Method stream(): take everything you can

This method is useful if you have a list of Optional<T>. Each list item is a case, or a container that can contain a “real” element or be empty. If you need the most simple way to get all the “real” items from this list, you can use the stream () method.

Imagine this situation. Every morning Ivan goes into his garden and gathers ripe fruits from every bush. On each bush, the vegetable can mature overnight, or it may not ripen. Therefore, we can simulate the yield of each hive using Optional<T>. For simplicity, we’ll use the T class String as the T .

Thus, the harvest of vegetables collected by Ivan every morning can be modeled as List<Optional<String>> getTomatoBeds () .

Suppose we want to get a list of vegetables  (of course ripened) in the form of an array.

Without the use of stream () , we would have to write for this purpose with for  … a loop, sort all “cases” in it, write “real” items to list a from there, rewrite them into an array.

In Java 8 for the array of ripened vegetables can be obtained as follows:

String[] result = tomatoGarden.getTomatoBeds()
        .stream()
        .filter(Optional::isPresent)
        .map(Optional::get)
        .toArray(String[]::new);

And in Java 9 it can be done even shorter and more elegant:

String[] result = tomatoGarden.getTomatoBeds()
        .stream()
        .flatMap(Optional::stream)
        .toArray(String[]::new);

Replacement of two lines by one was possible due to a call within flatMap  of the new method stream() , which is described in the documentation of Oracle

https://docs.oracle.com/javase/9/docs/api/java/util/Optional.html so:

Stream<T> stream​()
If a value is present, returns a sequential Stream containing only that value, otherwise returns an empty Stream.

In most cases, we use the stream ()  method to handle large data sequences. In the case of Optional<T>, we use stream () to process a maximum of one element at a time.

To fix one more example:

public void testOptionalStreamBase()  {
    Optional<String> opFilled = Optional.of("Filled");
    assertEquals(1, opFilled.stream().count());

    Optional<String> opEmpty  = Optional.empty();
    assertEquals(0, opEmpty.stream().count());
}

Metod ifPresentOrElse (): If you have not it, add it!

This new function closed the hole that remained in Java 8 and had to be compensated by a combination of calls to the methods ifPresent(Consumer<super T > action)  and orElse(T other) .

Let’s look again at the documentation for the new method:

void ifPresentOrElse​(Consumer<? superT> action, Runnable emptyAction)
If a value is present, performs the given action with the value, otherwise performs the given empty-based action.

As you can see, it allows you to process inside and the filled and empty “case” ( Optional<T> )

So let’s continue the story with Ivan. Ivan every morning goes into his garden, collects the ripened vegetables and puts them into a salad. To simulate this fact, we will use the setValue (String s) method. 

But if nothing has grown in the beds, he has to get canned vegetables from the can. To do this, we will use the setDefault()  method.

Here are their implementation:

private void setValue(String s){
    veg = s;}
private void setDefault(){
    veg = CANNED_FOOD;}

Variable veg models what will be in Ivan’s salad bowl.

And now let’s check how ifPresentOrElse()  works with the test:

@Test
public void testOptionalStreamIfPresentOrElse()  {
    Optional<String> optFilled = Optional.of(TOMATO);
    optFilled.ifPresentOrElse(this::setValue, this::setDefault);

    assertEquals(TOMATO, veg);

    Optional<String> optEmpty  = Optional.empty();
    optEmpty.ifPresentOrElse(this::setValue, this::setDefault);

    assertEquals(CANNED_FOOD, veg);
}

As we see, with the help of this model Ivan, though he did not find a fresh vegetable on the bed, but replaced it with a vegetable from a tin can.

Method or (): persistently looking for your happiness!

Attentive reader probably noticed that in the previous example, Ivan examined only the first bush. And how to simulate the situation, if there are a lot of bushes?

In this case, the or() method will help us. Look again at the documentation:

Optional<T> or​(Supplier<? extends Optional<? extends T>> supplier)
If a value is present, returns an Optional describing the value, otherwise returns an Optionalproduced by the supplying function.

In other words, with or(), you can build processing chains that will analyze the “cases” (Optional<T>) until the first non-empty element is encountered.

In the following example, optBed1, 2, 3 model vegetable beds or separate bushes.

@Test
public void testOptionalOr1()  {
    Optional<String> optBed1 = Optional.empty();
    Optional<String> optBed2 = Optional.of(TOMATO);
    Optional<String> optBed3 = Optional.of(CUCUMBER);
    String res = optBed1
            .or(()->{return optBed2;})
            .or(()->optBed3)
            .or(this::getDefault)
            .get();

    assertEquals(res, TOMATO);
}

Congratulations to Ivan, with the help of the new method or()  hefound a fresh tomato and he would not have to eat vegetables from a tin can.

Leave a Reply

Your email address will not be published. Required fields are marked *