orElse vs orElseGet in Java Optional (with Examples)

orElse vs orElseGet in Java explained with examples. Learn why orElse always computes its default, when orElseGet saves work and how to avoid side effects.

Timeline comparing orElse, which evaluates the default before the call, and orElseGet, which calls the Supplier only for an empty Optional

The difference between orElse() and orElseGet() is when the default value is computed. The orElse() method takes a ready value, so Java evaluates its argument every time, while orElseGet() takes a Supplier and calls it only when the Optional is empty. Both methods return the value inside the Optional when one is present.

We use orElse() for a constant or a value we already hold, such as a default theme name. We use orElseGet() when building the default costs time or changes something, such as reading a file, calling a service or inserting a database row.

The following example reads a saved theme with both methods. The ThemeStore class counts how often its loadDefault() method runs.

Optional<String> saved = Optional.of("dark");
ThemeStore store = new ThemeStore();

String eager = saved.orElse(store.loadDefault());       // "dark"
String lazy = saved.orElseGet(store::loadDefault);      // "dark"
int lookups = store.lookups();                          // 1, from the orElse() line

Notice that both calls return “dark”, yet loadDefault() ran once, for the orElse() line, even though a value was present. We look at the method parameters, the evaluation order, a real bug caused by an eager default, the performance cost, and the related methods orElseThrow() and or().

1. The Difference between orElse() and orElseGet() Methods

Both methods answer the same question, which value do we use when the Optional is empty. They differ in what they accept and therefore in when the default is built.

Timeline comparing orElse, which evaluates the default before the call, and orElseGet, which calls the Supplier only for an empty Optional
The argument of orElse() is computed before the method runs, whereas orElseGet() calls the Supplier only when the Optional is empty
FeatureorElse(T other)orElseGet(Supplier<? extends T> supplier)
ParameterA value of type T, which may be nullA Supplier that produces the value
When the default is computedAlways, before orElse() is called (eager)Only when the Optional is empty (lazy)
Side effects in the defaultRun on every callRun only for an empty Optional
Good fitConstants, literals, fields, values already computedMethod calls, I/O, object creation, logging, database access
Typical linetheme.orElse(“light”)theme.orElseGet(this::loadDefault)

1.1. Method Parameters

The orElse() method takes the default value itself. The orElseGet() method takes a Supplier, a functional interface with one method get() that returns the value, so we pass a lambda expression or a method reference.

A small ThemeStore class makes the difference visible. Its loadDefault() method stands in for any expensive lookup, and the lookups counter shows whether the lookup ran.

class ThemeStore {

    private int lookups;

    String loadDefault() {
        lookups++;                  // stands in for a file read or a remote call
        return "light";
    }

    int lookups() {
        return lookups;
    }
}

1.2. Evaluation of Default Value

The default value provided to orElse() is eagerly evaluated. Java evaluates every method argument before it calls the method, so store.loadDefault() runs first and its result is passed to orElse(). The orElse() method throws that result away when the Optional holds a value, but the work is already done.

Optional<String> saved = Optional.of("dark");
ThemeStore store = new ThemeStore();

String theme = saved.orElse(store.loadDefault());       // "dark"
int lookups = store.lookups();                          // 1, loadDefault() ran anyway

The supplier passed to orElseGet() is lazily evaluated. The method reference store::loadDefault creates a Supplier object, and orElseGet() calls its get() method only when no value is present.

Optional<String> saved = Optional.of("dark");
Optional<String> notSaved = Optional.empty();
ThemeStore store = new ThemeStore();

String theme = saved.orElseGet(store::loadDefault);     // "dark"
int before = store.lookups();                           // 0, the supplier was not called
String fallback = notSaved.orElseGet(store::loadDefault);  // "light"
int after = store.lookups();                            // 1

For an empty Optional, the two methods behave the same way. Both run the lookup once and return “light”, so the difference shows up only when a value is present.

2. When an Eager Default Causes a Bug

The extra call made by orElse() is more than wasted time when the default has a side effect. Any code that writes, sends or counts something runs on every call, including the calls where the Optional already has a value.

Say a shop has a get-or-create step at checkout. It looks up the customer by email, and creates a customer row only for a first-time buyer. The CustomerRepository class keeps rows in a list, so a duplicate insert stays visible.

class CustomerRepository {

    private final List<String> rows = new ArrayList<>();

    Optional<String> findByEmail(String email) {
        return rows.stream().filter(email::equals).findFirst();
    }

    String insert(String email) {
        rows.add(email);
        return email;
    }

    int rowCount() {
        return rows.size();
    }
}

With orElse(), the insert runs before the lookup result is checked. A returning customer gets a second row, and in a real database the same line either creates a duplicate or throws a unique-constraint error.

CustomerRepository repo = new CustomerRepository();
repo.insert("ana@mail.com");

String customer = repo.findByEmail("ana@mail.com")
        .orElse(repo.insert("ana@mail.com"));
int rows = repo.rowCount();                             // 2, a duplicate row

Switching to orElseGet() fixes the bug, because the insert moves inside the supplier and runs only for an unknown email.

CustomerRepository repo = new CustomerRepository();
repo.insert("ana@mail.com");

String known = repo.findByEmail("ana@mail.com")
        .orElseGet(() -> repo.insert("ana@mail.com"));
int rowsAfterKnown = repo.rowCount();                   // 1
String fresh = repo.findByEmail("raj@mail.com")
        .orElseGet(() -> repo.insert("raj@mail.com"));
int rowsAfterNew = repo.rowCount();                     // 2, one row per customer

The same rule applies to defaults that log a warning, increment a metric, send an event or open a connection. In code reviews, we read the argument of orElse() as code that always runs, which makes a call such as orElse(createSomething()) stand out.

3. Performance Impact

When the default is a literal, a constant or a variable, orElse() costs nothing extra, because the value already exists. When the default is a method call, orElse() pays for that call every time, and the cost grows with the method, such as a file read, a remote call or a large object allocation.

The orElseGet() method has a small cost of its own. The JVM creates a Supplier object for the lambda, although a lambda that captures no variables is created once and reused. A lambda that captures local variables, such as () -> repo.insert(email), may need a new small object each time the line runs.

Benchmarks that show orElseGet() thousands of times faster measure the fallback method, not Optional. For a constant default, the choice between the two methods makes no measurable difference, so readability decides.

4. When to Use orElse() or orElseGet()?

We use orElse() when the default value already exists or is a cheap constant. We use orElseGet() when building the default calls a method, creates an object or has a side effect.

Default valueMethodExample
String or number literalorElse()orElse(“light”)
Constant or fieldorElse()orElse(DEFAULT_THEME)
null for legacy APIsorElse()orElse(null)
Method call or calculationorElseGet()orElseGet(this::loadDefault)
New objectorElseGet()orElseGet(ArrayList::new)
Database write, remote call, loggingorElseGet()orElseGet(() -> repo.insert(email))
Missing value is an errororElseThrow()orElseThrow(() -> new NotFoundException(id))

If the argument of orElse() contains parentheses, we check whether it calls a method, and if it does, we switch to orElseGet(). An object creation such as new ArrayList<>() counts too, because it runs a constructor.

5. Related Methods orElseThrow() and or()

Two more methods handle an empty Optional, and they cover cases where neither default method fits. The orElseThrow() method fails fast when a missing value is a bug, and or() tries another source that also returns an Optional.

  • The no-argument orElseThrow() (Java 10) throws NoSuchElementException and replaces get().
  • The orElseThrow(Supplier) form throws the exception we create, and like orElseGet() it builds the exception only for an empty Optional.
  • The or(Supplier) method (Java 9) returns another Optional, so we can chain fallbacks, such as a team setting when the user has no setting of their own.
Optional<String> userTheme = Optional.empty();
Optional<String> teamTheme = Optional.of("system");

String theme = userTheme.or(() -> teamTheme).orElse("light");          // "system"
String required = userTheme.orElseThrow();                            // NoSuchElementException: No value present
String explained = userTheme.orElseThrow(() -> new IllegalStateException("No theme"));  // IllegalStateException: No theme

The Java Optional guide covers these methods together with map(), filter(), stream() and the places where Optional does not belong.

6. orElse() vs orElseGet() FAQs

Eager evaluation surprises developers in two places, null defaults and code that looks harmless inside the argument.

6.1. Is orElseGet() Always Better than orElse()?

No. For a literal, a constant or a variable we already hold, orElse() is shorter and does no extra work. The orElseGet() method helps only when building the default runs code, so wrapping a constant in a lambda, as in orElseGet(() -> “light”), adds noise without a benefit.

6.2. Does orElse() Call the Method When the Optional Has a Value?

Yes. Java evaluates the argument before calling orElse(), so a method call inside the parentheses runs even when the value is present. The orElse() method ignores the result, but any side effect of the call has already happened, as the duplicate row in section 2 shows.

6.3. Can orElse() or orElseGet() Return null?

Yes. The orElse() method accepts null as the default, and orElseGet() returns whatever the supplier returns, including null. A null supplier is different, because orElseGet() throws NullPointerException when it needs to call it.

Optional<String> none = Optional.empty();
Supplier<String> missing = null;

String fromOrElse = none.orElse(null);                  // null
String fromSupplier = none.orElseGet(() -> null);       // null
String present = Optional.of("dark").orElseGet(missing);  // "dark", supplier not needed
String broken = none.orElseGet(missing);                // NullPointerException

6.4. When Do We Use or() Instead of orElseGet()?

The orElseGet() method returns a plain value of type T and ends the chain. The or() method returns an Optional<T>, so the fallback may also be empty and we can add more fallbacks or call map() afterwards. Both take a Supplier and call it only for an empty Optional.

6.5. Should I Use orElseThrow() Instead of orElseGet()?

Yes, when a missing value means the request cannot continue, for example an order ID that must exist. Returning a made-up default in that case hides the problem, while orElseThrow() stops with a clear exception that a REST controller can turn into a 404 response.

7. Conclusion

Both orElse() and orElseGet() return the value of a present Optional and a default for an empty one. The orElse() method takes the default as a value, so its argument is always computed, while orElseGet() takes a Supplier and computes the default only when it is needed.

For literals and constants, orElse() is the clearer choice. For method calls, new objects and anything with side effects, such as a database insert, we use orElseGet(), and when a missing value is an error, orElseThrow().

8. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.