Java Optional Default Values: orElse vs orElseGet

Java `Optional` provides `orElse` and `orElseGet` for supplying a fallback when a value is absent. The key difference is evaluation: `orElse` evaluates its argument immediately, while `orElseGet` invokes its supplier only when the `Optional` is empty.

`Optional<T>` represents either a present value or the absence of one. When you need a fallback, `orElse(value)` supplies a concrete fallback, while `orElseGet(supplier)` accepts a function that can produce the fallback.

The important distinction is when the fallback expression runs. With `orElse`, the argument expression is evaluated before the method receives it, even if the `Optional` already contains a value. With `orElseGet`, the supplier is called only when the `Optional` has no value.

This matters when creating the fallback is expensive, has side effects, or calls another method. For a cheap constant such as `"unknown"`, `orElse` is usually straightforward. For an operation such as loading a record from a database, `orElseGet` avoids unnecessary work when the value is already present.

Neither method changes the `Optional` itself. Both return either the contained value or the fallback result, and both can return `null` if the supplied fallback itself evaluates to `null`.

Key points

Interview questions

What is the difference between `orElse` and `orElseGet`?

`orElse` takes a value as its fallback, while `orElseGet` takes a `Supplier`. The expression used to produce the argument to `orElse` is evaluated regardless of whether the `Optional` contains a value. The supplier passed to `orElseGet` is invoked only when the `Optional` is empty. Therefore, `orElseGet` is useful when fallback creation is costly or has side effects.

Why can `orElse` perform unnecessary work?

Java evaluates method arguments before the method call. Therefore, in `optional.orElse(createDefault())`, `createDefault()` runs even when `optional` already contains a value. If that method performs expensive computation, allocates resources, or performs I/O, that work is unnecessary. `orElseGet(() -> createDefault())` defers the call until the fallback is actually required.

When would you use `orElse` instead of `orElseGet`?

I would generally use `orElse` when the fallback is already available or is cheap to compute, such as a literal string or simple constant. It is concise and clearly expresses that default value. If producing the fallback requires meaningful computation or an external operation, I would use `orElseGet` instead. The choice should reflect the cost and behavior of evaluating the fallback.

What happens if the `Optional` is empty with `orElseGet`?

If the `Optional` is empty, `orElseGet` invokes the supplied `Supplier` and returns the supplier's result. For example, `Optional.empty().orElseGet(() -> "default")` returns `"default"`. If the supplier itself throws an exception, that exception propagates to the caller. The supplier is therefore a conditional source of the fallback value.

Can `orElse` and `orElseGet` return different results?

They can if the fallback computation has observable behavior or depends on changing state. With a pure, deterministic fallback, both normally produce the same fallback result when the `Optional` is empty. When the `Optional` is present, both return its contained value, but `orElse` still evaluates its fallback expression first. This difference makes side-effecting fallback expressions particularly important to avoid.