What do Optional.or and Optional.stream do, and when would you reach for each?
answer
- or(Supplier<Optional>) -> fallback that stays Optional (lazy)
- or chains multi-source lookups, unwrap at the end
- stream() -> 0 or 1 element stream
- flatMap(Optional::stream) filters+unwraps present values
- Replaces filter(isPresent).map(get) idiom
basics
~20 sor (Java 9+) supplies an alternative Optional when the first is empty, so the result stays an Optional. stream (Java 9+) turns an Optional into a stream of zero or one element, which is handy for flat-mapping a stream of Optionals down to just the present values.
solid answer
~40 sBoth were added in Java 9. Optional.or(Supplier<Optional<T>>) provides a fallback that is itself an Optional: if the original is present it is returned, otherwise the supplier is invoked and its Optional is returned. Unlike orElse/orElseGet, the result is still an Optional, so you can chain several sources of truth (cache, then DB, then default) and only unwrap at the end. The supplier is lazy, so later sources are queried only when earlier ones are empty. Optional.stream() converts an Optional into a Stream of zero or one element — empty Optional gives an empty stream, present gives a single-element stream. Its main use is inside Stream.flatMap to filter and unwrap in one step: stream.map(this::lookup).flatMap(Optional::stream) keeps only the present results, replacing the older filter(Optional::isPresent).map(Optional::get) idiom.
code
java · 10 lines// or: layered fallbacks, result stays Optional
Optional<String> name = fromHeader(req)
.or(() -> fromCookie(req))
.or(() -> fromSession(req));
// stream: collapse Stream<Optional<User>> to present users, no get()
List<User> users = ids.stream()
.map(this::findUser) // Stream<Optional<User>>
.flatMap(Optional::stream) // Stream<User>
.toList();go deeper
Recognizes that or supplies an alternative Optional and stream() yields a 0/1-element stream.
Uses flatMap(Optional::stream) to unwrap a Stream<Optional<T>> and or for a simple two-source fallback.
Designs layered multi-source lookups with lazy or chains and replaces the legacy isPresent/get filtering idiom.
Weighs or-chains vs explicit control flow for readability, and standardizes get-free Optional/Stream interop across a codebase.
### Both are Java 9 additions `or` and `stream` filled gaps in the original Java 8 `Optional` API. ### Optional.or — fallback that stays an Optional ```java Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) ``` Contrast with the fallbacks you already know: - `orElse(T)` / `orElseGet(Supplier<T>)` unwrap to a **plain value** `T`. - `or(Supplier<Optional<T>>)` returns **another `Optional<T>`**. Behavior: if `this` is present, return `this`; if empty, call the supplier and return the `Optional` it produces. The supplier is **lazy** — invoked only when `this` is empty. Why you want an `Optional` result: it lets you chain multiple *sources*, each of which may itself be absent, and defer the final decision: ```java Optional<Config> cfg = fromCache(key) .or(() -> fromDatabase(key)) .or(() -> fromDefaults(key)); // still an Optional<Config>: maybe all sources were empty Config result = cfg.orElseThrow(() -> new MissingConfigException(key)); ``` Each `or` is tried in order, stopping at the first present source. With `orElseGet` you would have had to unwrap prematurely or nest awkwardly. ### Optional.stream — bridge to the Stream API ```java Stream<T> stream() ``` It converts: - an **empty** `Optional` -> an **empty** `Stream` (0 elements) - a **present** `Optional` -> a **single-element** `Stream` (1 element) On its own that seems minor, but it shines with `Stream.flatMap`. Suppose you have a stream of ids and a lookup that returns `Optional<User>`: ```java List<User> users = ids.stream() .map(this::findUser) // Stream<Optional<User>> .flatMap(Optional::stream) // Stream<User> — empties vanish .toList(); ``` `flatMap(Optional::stream)` expands each `Optional` into 0 or 1 elements, so absent results contribute nothing and present results contribute their value — **filtering and unwrapping in a single, type-safe step**. ### The idiom it replaces Before Java 9 people wrote: ```java .filter(Optional::isPresent) .map(Optional::get) ``` This works but uses the discouraged `get()` and is two operations. `flatMap(Optional::stream)` is the modern, get-free replacement. ### When to reach for each - **`or`**: you have several Optional-producing fallbacks and want to keep the result optional until the very end (multi-source lookups, layered configuration). Lazy ordering matters. - **`stream`**: you are already in a `Stream` pipeline and need to collapse a `Stream<Optional<T>>` down to the present `T`s without `get()`. ### Edge notes - `or`'s supplier may itself return an empty `Optional`; the chain simply continues to the next `or` (or ends empty). - `Optional.stream()` produces a sequential stream of at most one element; it is cheap and creates no intermediate collection.
- How does or differ from orElseGet?orElseGet's supplier returns a plain value T and the method unwraps to T. or's supplier returns an Optional<T> and the method returns an Optional<T>, so you can keep chaining further or stages before unwrapping.
- Why prefer flatMap(Optional::stream) over filter(Optional::isPresent).map(Optional::get)?It is one operation instead of two, avoids the discouraged get(), and is type-safe end to end — an empty Optional simply contributes zero elements.
or is a phone tree: try the cache, no answer? try the DB, no answer? try defaults — you keep an 'are we connected yet?' status (still an Optional) until someone picks up. stream() is turning a maybe-package into a conveyor of 0 or 1 boxes so it merges into a larger conveyor.
saying these in an interview costs you the question
- Thinking or unwraps to a plain value — it returns an Optional
- Believing or eagerly evaluates all sources — the supplier is lazy and later sources run only if earlier ones are empty
- Assuming Optional.stream() existed in Java 8 — it (and or) arrived in Java 9
- Reaching for filter(isPresent).map(get) in modern code