В чем разница между orElse() и orElseGet() в Java Optional?

Ответ

Оба метода предоставляют значение по умолчанию для пустого Optional, но различаются моментом вычисления этого значения, что напрямую влияет на производительность.

orElse(T other)

  • Вычисляет значение other немедленно, в момент вызова метода, независимо от того, пуст Optional или нет.
  • Подходит для простых, дешевых констант или предварительно вычисленных значений.
Optional<String> foundName = findUserInCache(userId);
// Дорогой вызов БД произойдет ВСЕГДА, даже если пользователь найден в кэше
String name = foundName.orElse(fetchUserFromDatabase(userId)); // Неоптимально!

orElseGet(Supplier supplier)

  • Вычисляет значение лениво, только если Optional действительно пуст.
  • Принимает Supplier — функциональный интерфейс, код которого выполняется по требованию.
  • Предпочтительный выбор для ресурсоемких операций (запросы к БД, сетевые вызовы, сложные вычисления).
Optional<String> foundName = findUserInCache(userId);
// Вызов fetchUserFromDatabase произойдет ТОЛЬКО если кэш пуст
String name = foundName.orElseGet(() -> fetchUserFromDatabase(userId)); // Оптимально

Ключевое отличие на примере

public String getDefaultValue() {
    System.out.println("Вычисляю значение по умолчанию...");
    return "Default";
}

Optional<String> fullOpt = Optional.of("Actual Value");
Optional<String> emptyOpt = Optional.empty();

// С orElse()
String result1 = fullOpt.orElse(getDefaultValue()); // Выведет: "Вычисляю значение по умолчанию..."
// result1 = "Actual Value", но побочный эффект выполнен!

String result2 = emptyOpt.orElse(getDefaultValue()); // Выведет: "Вычисляю значение по умолчанию..."
// result2 = "Default"

// С orElseGet()
String result3 = fullOpt.orElseGet(() -> getDefaultValue()); // Ничего не выведет!
// result3 = "Actual Value", вычисления не было

String result4 = emptyOpt.orElseGet(() -> getDefaultValue()); // Выведет: "Вычисляю значение по умолчанию..."
// result4 = "Default"

Вывод: Используйте orElseGet() для отложенных вычислений, особенно когда получение значения по умолчанию — дорогая операция.

Ответ 18+ 🔞

А, слушай, вот тебе реальная разница, которую половина народа на собесах проваливает, а потом в продакшене тормозит всё, как черепаха в патруле. Смотри, оба метода вроде как дают дефолтное значение, если в Optional нихуя нет, но тут, бля, вся соль в моменте, когда эта дефолтная хуйня вычисляется. И это, ёпта, напрямую бьёт по производительности, если не понимать.

orElse(T other)

Этот метод — как тот назойливый друг, который приходит в гости, даже если ты ему не звонил. Значение other вычисляется сразу, в момент вызова метода. Неважно, пустой там Optional или полный — ты уже за него заплатил по полной программе. Ну, ок, если там константа типа "N/A" — похуй. А если там вызов к базе данных?

Optional<String> foundName = findUserInCache(userId);
// Вот эта хуйня с запросом в БД выполнится ВСЕГДА, ёбаный насос!
// Даже если пользователь уже в кэше и всё ок.
String name = foundName.orElse(fetchUserFromDatabase(userId)); // Пиздец, а не оптимизация!

orElseGet(Supplier supplier)

А вот это уже хитрая жопа. Работает лениво, по требованию. Ты ему передаёшь не готовое значение, а Supplier — этакую обещашку, инструкцию "как достать значение, если приспичит". И выполнится эта инструкция только если Optional реально пустой. Для дорогих операций — просто манна небесная.

Optional<String> foundName = findUserInCache(userId);
// А вот этот вызов в базу случится ТОЛЬКО если в кэше пусто.
// Умно, экономно, мать его.
String name = foundName.orElseGet(() -> fetchUserFromDatabase(userId)); // Вот так, бля, правильно!

Ключевое отличие, чтоб ты совсем охуел

Смотри на примере, тут всё станет ясно, как божий день.

public String getDefaultValue() {
    System.out.println("Вычисляю значение по умолчанию..."); // Дорогая операция, блядь!
    return "Default";
}

Optional<String> fullOpt = Optional.of("Actual Value");
Optional<String> emptyOpt = Optional.empty();

// С orElse() — тут пиздец начинается
String result1 = fullOpt.orElse(getDefaultValue()); // Консоль: "Вычисляю значение по умолчанию..."
// result1 = "Actual Value", но мы нихуя не сэкономили, вызов-то уже произошёл!

String result2 = emptyOpt.orElse(getDefaultValue()); // Консоль: "Вычисляю значение по умолчанию..."
// result2 = "Default"

// С orElseGet() — а вот тут уже умно
String result3 = fullOpt.orElseGet(() -> getDefaultValue()); // Тишина в консоли! Ничего не вывело!
// result3 = "Actual Value", и ни одной лишней операции, красота.

String result4 = emptyOpt.orElseGet(() -> getDefaultValue()); // Консоль: "Вычисляю значение по умолчанию..."
// result4 = "Default"

Итог, ёпта: Не будь распиздяем. Если получение значения по умолчанию — это что-то тяжёлое (база, сетевой запрос, сложный расчёт), всегда юзай orElseGet(). Сэкономишь ресурсы, и система не будет орать как резаная, когда нагрузка подскочит. orElse() оставь для простых констант, где похуй.