Back to Optional
Q7Question 7 of 9

What did Java 9 add to Optional?

Key Concepts
  • Optional.stream() converts a present Optional to a one-element Stream and an empty Optional to an empty Stream
  • Used with flatMap(Optional::stream) to cleanly drop empty Optionals from a stream pipeline
  • Replaces the verbose filter(Optional::isPresent).map(Optional::get) pattern
  • Optional.or(Supplier) provides a lazy fallback Optional instead of a fallback value
  • or() returns another Optional enabling further chaining, unlike orElse which returns a raw value
  • Optional.ifPresentOrElse(action, emptyAction) handles both present and empty cases in one call
  • Together these three methods make Optional a complete monadic container for stream integration

Definition

Java 9 added Optional.stream() for stream integration, or() for lazy fallback chains, and ifPresentOrElse() for handling both present and empty cases.
  • stream() – present Optional becomes a one-element Stream; empty becomes an empty Stream. Used with flatMap(Optional::stream) to drop empties.
  • or(Supplier>) – provides a lazy fallback Optional if this one is empty; enables chaining multiple lookup sources.
  • ifPresentOrElse(action, emptyAction) – executes one action if present, another if empty; replaces if (opt.isPresent()) { ... } else { ... }.

stream() is like opening envelopes on a conveyor belt: envelopes with cheques (present) put the cheque on the belt, empty envelopes put nothing. or() is like trying multiple bank branches: check savings, not found? Try current, not found? Try NRI. ifPresentOrElse is the teller saying: if the cheque exists, deposit it; if not, log a missing-cheque alert.

Understanding the Concepts

What did Java 9 add to Optional?

Three methods improve Optional's integration with streams and fallback handling.

How does Optional.stream() work?

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;

public class AmountParser {

    // BEFORE Java 9: verbose
    List<BigDecimal> parseAll(List<String> inputs) {
        return inputs.stream()
            .map(this::tryParse)
            .filter(Optional::isPresent)
            .map(Optional::get)  // unsafe .get()!
            .toList();
    }

    // AFTER Java 9: clean
    List<BigDecimal> parseAllClean(List<String> inputs) {
        return inputs.stream()
            .map(this::tryParse)
            .flatMap(Optional::stream)  // present → value, empty → gone
            .toList();
    }

    private Optional<BigDecimal> tryParse(String input) {
        try {
            return Optional.of(new BigDecimal(input));
        } catch (NumberFormatException e) {
            return Optional.empty();
        }
    }
}

How does Optional.or() work?

JAVA
// Try primary source. If empty, try secondary. If empty, try tertiary.
Optional<Account> account = findInSavings(accountId)
    .or(() -> findInCurrent(accountId))    // lazy: only called if savings is empty
    .or(() -> findInNRI(accountId));        // lazy: only called if current is also empty

// Key: or() returns Optional, enabling further chaining
// orElse() returns the VALUE, ending the chain
Account value = findInSavings(id).orElse(defaultAccount);  // returns Account
Optional<Account> opt = findInSavings(id).or(() -> findInCurrent(id));  // returns Optional

How does Optional.ifPresentOrElse() work?

JAVA
// BEFORE: if-else
Optional<Account> account = findAccount(id);
if (account.isPresent()) {
    processAccount(account.get());
} else {
    log.warn("Account {} not found", id);
}

// AFTER: one method call
findAccount(id).ifPresentOrElse(
    this::processAccount,
    () -> log.warn("Account {} not found", id));

What is the difference between or, orElse, and orElseGet?

| Method | Returns | Evaluation | Purpose |
|---|---|---|---|
| orElse(value) | T | Eager (always evaluated) | Default value |
| orElseGet(supplier) | T | Lazy (only if empty) | Computed default |
| or(supplier) | Optional | Lazy (only if empty) | Fallback Optional chain |

What should a senior engineer know?

  • stream() bridges Optional to Stream: the pattern flatMap(Optional::stream) is the idiomatic way to drop empties.
  • or() suppliers should be side-effect-free; they may or may not execute depending on whether earlier Optionals are present.
  • ifPresentOrElse returns void; it is for side effects only, not for computing values.

What are common interview follow-up questions?

  • Why is flatMap(Optional::stream) better than filter+get? (No unsafe .get() call. One method reference instead of two lines.)
  • Can you chain or() multiple times? (Yes. Each supplier is tried in order until one returns a present Optional.)
  • Does ifPresentOrElse return a value? (No. It returns void. For computed values, use map or flatMap.)

Visual Overview

DIAGRAM
  Optional.stream():
  Optional.of("SAV2001")  → Stream.of("SAV2001")  (1 element)
  Optional.empty()        → Stream.empty()          (0 elements)

  flatMap(Optional::stream) pipeline:
  Stream:  Optional.of(100) → Optional.empty() → Optional.of(300)
                │                    │                  │
                ▼                    ▼                  ▼
  flatMap:   Stream(100)      Stream.empty()     Stream(300)
  Result:   [100, 300]  (empties dropped)

  Optional.or() chain:
  findInSavings(id)   → empty
    .or(() -> findInCurrent(id))  → empty
    .or(() -> findInNRI(id))      → present(Account)
  Result: Account from NRI

Key Takeaway

Java 9 added three methods to Optional that improve stream integration and fallback handling. Optional.stream() converts a present Optional to a one-element Stream and an empty Optional to an empty Stream. Used with flatMap(Optional::stream), it cleanly drops empty Optionals from a pipeline, replacing the verbose filter(Optional::isPresent).map(Optional::get) pattern.

Optional.or(Supplier) provides a lazy fallback Optional instead of a fallback value. Each supplier is only called if the previous Optional was empty. Unlike orElse which returns a value, or() returns another Optional, enabling further chaining with map, filter, or another or.

ifPresentOrElse(action, emptyAction) handles both present and empty cases in one method call. The present action receives the value; the empty action is a Runnable. Together, these three methods make Optional a complete monadic container for stream integration.

Remember one sentence: Optional.stream() bridges Optional to Stream via flatMap, or() enables lazy fallback chains, and ifPresentOrElse handles both cases in one call.

---

Share
Use ← → arrow keys to navigate