GotchaDifficulty: Intermediate1 min

orElse Runs Its Fallback Even When the Value Is Present

A cache that hits every time, and a database query in the logs for every hit.

The argument to orElse is computed before orElse is called, so a fallback written there runs every time, even when the Optional holds a value and the fallback is thrown away. orElseGet takes a lambda, an inline function such as () -> load(), instead, and runs it only when the value is missing.

The symptom: a cache lookup that hits every time, and a database query in the logs for every one of those hits. The fallback is inside an orElse that should never be reached:

java
Customer c = cached(ref).orElse(loadFromDatabase(ref));

orElse is an ordinary method taking an ordinary argument, and Java evaluates arguments before calling. loadFromDatabase(ref) runs first, every time, and its result is then handed to an orElse that usually discards it.

What runs, with a value present
WrittenThe fallback runsThe result
orElse(loadFromDatabase(ref))Yes, and is thrown awayThe cached value
orElseGet(() -> loadFromDatabase(ref))NoThe cached value

The lambda in orElseGet is what defers the work: nothing inside it runs until orElseGet decides to call it. Use orElse only for a value you already have.

java
Customer c = cached(ref).orElseGet(() -> loadFromDatabase(ref));