1. Overview

In this article, we will be looking at the Java 9 additions to the Optional API.

Beyond modularity, Java 9 is also adding three very useful methods for the Optional class.

2. The or() Method

Sometimes, when our Optional is empty, we want to execute some other action that also returns an Optional.

Prior Java 9 the Optional class had only the orElse() and orElseGet() methods but both need to return unwrapped values.

Java 9 introduces the or() method that returns another Optional lazily if our Optional is empty. If our first Optional has a defined value, the lambda passed to the or() method will not be invoked, and value will not be calculated and returned:

@Test
public void givenOptional_whenPresent_thenShouldTakeAValueFromIt() {
    //given
    String expected = "properValue";
    Optional<String> value = Optional.of(expected);
    Optional<String> defaultValue = Optional.of("default");

    //when
    Optional<String> result = value.or(() -> defaultValue);

    //then
    assertThat(result.get()).isEqualTo(expected);
}

In the case of Optional being empty, the returned result will be the same as the defaultValue:

@Test
public void givenOptional_whenEmpty_thenShouldTakeAValueFromOr() {
    // given
    String defaultString = "default";
    Optional<String> value = Optional.empty();
    Optional<String> defaultValue = Optional.of(defaultString);

    // when
    Optional<String> result = value.or(() -> defaultValue);

    // then
    assertThat(result.get()).isEqualTo(defaultString);
}

3. The ifPresentOrElse() Method

When we have an Optional instance, often we want to execute a specific action on the underlying value of it. On the other hand, if the Optional is empty we want to log it or track that fact by incrementing some metric.

The ifPresentOrElse() method is created exactly for such scenarios. We can pass a Consumer that will be invoked if the Optional is defined, and Runnable that will be executed if the Optional is empty.

Let’s say that we have a defined Optional and we want to increment a specific counter if the value is present:

@Test
public void givenOptional_whenPresent_thenShouldExecuteProperCallback() {
    // given
    Optional<String> value = Optional.of("properValue");
    AtomicInteger successCounter = new AtomicInteger(0);
    AtomicInteger onEmptyOptionalCounter = new AtomicInteger(0);

    // when
    value.ifPresentOrElse(
      v -> successCounter.incrementAndGet(), 
      onEmptyOptionalCounter::incrementAndGet);

    // then
    assertThat(successCounter.get()).isEqualTo(1);
    assertThat(onEmptyOptionalCounter.get()).isEqualTo(0);
}

Note, that the callback passed as the second argument was not executed.

In the case of an empty Optional, the second callback gets executed:

@Test
public void givenOptional_whenNotPresent_thenShouldExecuteProperCallback() {
    // given
    Optional<String> value = Optional.empty();
    AtomicInteger successCounter = new AtomicInteger(0);
    AtomicInteger onEmptyOptionalCounter = new AtomicInteger(0);

    // when
    value.ifPresentOrElse(
      v -> successCounter.incrementAndGet(), 
      onEmptyOptionalCounter::incrementAndGet);

    // then
    assertThat(successCounter.get()).isEqualTo(0);
    assertThat(onEmptyOptionalCounter.get()).isEqualTo(1);
}

4. The stream() Method

The last method, which is added to the Optional class in the Java 9, is the stream() method.

Java has a very fluent and elegant Stream API that can operate on the collections and utilizes many functional programming concepts. The newest Java version introduces the stream() method on the Optional class that allows us to treat the Optional instance as a Stream.

Let’s say that we have a defined Optional and we are calling the stream() method on it. This will create a Stream of one element on which we can use all the methods that are available in the Stream API*:*

@Test
public void givenOptionalOfSome_whenToStream_thenShouldTreatItAsOneElementStream() {
    // given
    Optional<String> value = Optional.of("a");

    // when
    List<String> collect = value.stream().map(String::toUpperCase).collect(Collectors.toList());

    // then
    assertThat(collect).hasSameElementsAs(List.of("A"));
}

On the other hand, if Optional is not present, calling the stream() method on it will create an empty Stream:

@Test
public void givenOptionalOfNone_whenToStream_thenShouldTreatItAsZeroElementStream() {
    // given
    Optional<String> value = Optional.empty();

    // when
    List<String> collect = value.stream()
      .map(String::toUpperCase)
      .collect(Collectors.toList());

    // then
    assertThat(collect).isEmpty();
}

We can now quickly filter Streams of Optionals.

Operating on the empty Stream will not have any effect, but thanks to the stream() method we can now chain the Optional API with the Stream API. This allows us to create more elegant and fluent code.

5. Conclusion

In this quick article, we looked at the Java 9 Optional API additions.

We saw how to use the or() method to return an Optional in case if the source Optional is empty. We used the ifPresentOrElse() to execute the Consumer if the value is present and otherwise, run the another callback.

Finally, we saw how to chain the Optional with the Stream API using the stream() method.

The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.