What is the difference between Optional.orElse and Optional.orElseGet, and when does it matter?
answer
- orElse = eager value, always evaluated
- orElseGet = lazy supplier, only on empty
- Expensive/side-effecting fallback -> orElseGet
- Cheap constant -> orElse for readability
basics
~20 sorElse takes a ready value and always evaluates it, even when the Optional has a value. orElseGet takes a function that produces the fallback only when the Optional is empty. Use orElseGet when the fallback is expensive.
solid answer
~40 sBoth supply a fallback when an Optional is empty, but they differ in evaluation timing. orElse(T) takes an already-computed value, so its argument expression is always evaluated, even when the Optional is present and the fallback is discarded. orElseGet(Supplier<T>) takes a supplier (a no-arg function) that is invoked lazily, only when the Optional is empty. The difference matters when the fallback is expensive (a DB call, object allocation) or has side effects: with orElse you pay that cost on every call regardless; with orElseGet you pay it only on the empty path. For a cheap constant like orElse("") or orElse(0) the difference is negligible and orElse reads more cleanly. Rule of thumb: cheap constant -> orElse; anything computed, allocating, or side-effecting -> orElseGet.
code
java · 12 linesOptional<String> name = Optional.of("Ada");
// expensiveDefault() runs even though name is present (wasted work):
String a = name.orElse(expensiveDefault());
// expensiveDefault() is NOT called, because name is present:
String b = name.orElseGet(() -> expensiveDefault());
static String expensiveDefault() {
System.out.println("computing default..."); // side effect proves the point
return "fallback";
}go deeper
Knows both supply a default when empty and can pick orElse for a constant.
Explains eager vs lazy evaluation and picks orElseGet for expensive or side-effecting fallbacks.
Articulates the argument-evaluation semantics, allocation implications, and the side-effect-on-present-path bug class.
Frames it as a general lazy-vs-eager API design lesson and can spot orElse(new ...) allocation hotspots in code review.
### What is Optional? `Optional<T>` is a container object introduced in Java 8 that holds either one non-null value ("present") or no value ("empty"). Its purpose is to make the possible absence of a value explicit in the type, instead of returning `null` and risking a `NullPointerException`. You typically receive an `Optional` from an API and then need to extract a real value out of it, providing a fallback for the empty case. ### The two fallback methods Two methods provide a default when the `Optional` is empty: - `T orElse(T other)` — you pass an **already-computed value**. Because Java evaluates method arguments *before* the method is called (eager, applicative evaluation), the expression you pass is computed **every time**, whether or not the `Optional` actually has a value. - `T orElseGet(Supplier<? extends T> supplier)` — you pass a **`Supplier`**, which is a functional interface with one method `T get()` taking no arguments. The supplier's body is only executed **when the `Optional` is empty**, because `orElseGet` internally calls `supplier.get()` only on the empty branch. This is called *lazy* evaluation. ### Why the difference matters Consider: ```java String name = optionalName.orElse(loadDefaultFromDatabase()); ``` Even if `optionalName` is present, `loadDefaultFromDatabase()` still runs, because its result has to be computed to be passed as the argument. That is wasted work — and if it has side effects (logging, inserting a row, mutating state) those side effects happen even when not needed. With: ```java String name = optionalName.orElseGet(() -> loadDefaultFromDatabase()); ``` the lambda is wrapped in a `Supplier` and only invoked if `optionalName` is empty. No wasted call on the present path. ### When each is appropriate - Use **`orElse`** for a cheap, constant, side-effect-free fallback: `orElse("")`, `orElse(0)`, `orElse(Collections.emptyList())` (a shared immutable instance). It is shorter and clearer. - Use **`orElseGet`** whenever the fallback is **computed, allocates a new object, performs I/O, or has side effects**. It avoids both the cost and accidental side effects on the present path. ### Edge cases - Both return `T`. If the supplied default is itself `null`, you get `null` back — neither method protects against a null *default*. - `orElse(new ArrayList<>())` quietly allocates a fresh list on every call; for high-frequency code prefer `orElseGet(ArrayList::new)` so the allocation only happens when actually needed. - There is also `orElseThrow()` / `orElseThrow(Supplier)` for the case where empty is an error rather than a value to substitute.
- If the Optional is empty, do orElse and orElseGet return the same thing?Yes — both return the supplied default on the empty path. The behavioral difference is only on the present path (whether the fallback is computed at all).
- Does orElse protect against a null fallback?No. If you pass null (or a supplier returning null), you get null back. Neither method guards the default value itself.
orElse is like buying a backup umbrella before checking the weather — you pay for it regardless. orElseGet is buying one only if it actually rains.
saying these in an interview costs you the question
- Claiming orElseGet is 'always faster' — for a cheap constant they are effectively identical and orElse is clearer
- Thinking orElse short-circuits when the value is present — its argument is always evaluated
- Believing the difference is about thread-safety or atomicity — it is purely about evaluation timing