Вопрос-ответ

Difference between `Optional.orElse()` and `Optional.orElseGet()`

Разница между `Optional.OrElse ()` и `Optional.orElseGet()`

Я пытаюсь понять разницу между Optional<T>.orElse() и Optional<T>.orElseGet() методами.

Описание orElse() метода таково "Возвращает значение, если оно присутствует, в противном случае возвращает другое".

В то время как описание для orElseGet() метода является "Верните значение, если оно присутствует, в противном случае вызовите other и верните результат этого вызова".

orElseGet() Метод использует функциональный интерфейс поставщика, который, по сути, не принимает никаких параметров и возвращает T.

В какой ситуации вам нужно было бы использовать orElseGet()? Если у вас есть метод, T myDefault() почему бы вам просто не сделать optional.orElse(myDefault()) вместо optional.orElseGet(() -> myDefault()) ?

Не похоже, что orElseGet() откладывает выполнение лямбда-выражения на какое-то более позднее время или что-то в этом роде, так в чем же смысл этого? (Я бы подумал, что было бы полезнее, если бы он возвращал более безопасный, Optional<T> который get() никогда не выдает NoSuchElementException и isPresent() всегда возвращает true... но, очевидно, это не так, он просто возвращает T like orElse()).

Есть ли еще какое-то отличие, которое я упускаю?

Переведено автоматически
Ответ 1

Краткий ответ:


  • OrElse () всегда будет вызывать данную функцию, хотите вы этого или нет, независимо от Optional.isPresent() значения

  • orElseGet () будет вызывать данную функцию только тогда, когда Optional.isPresent() == false

В реальном коде вам может потребоваться рассмотреть второй подход, когда получение требуемого ресурса дорого.

// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource());

// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource())

Для получения более подробной информации рассмотрим следующий пример с этой функцией:

public Optional<String> findMyPhone(int phoneId)

Разница в том, как показано ниже:

                           X : buyNewExpensivePhone() called

+——————————————————————————————————————————————————————————————————+——————————————+
| Optional.isPresent() | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone()) | X | X |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) | | X |
+——————————————————————————————————————————————————————————————————+——————————————+

Когда optional.isPresent() == false, разницы между двумя способами нет. Однако, когда optional.isPresent() == true, orElse() всегда вызывает последующую функцию, хотите вы этого или нет.

Наконец, используемый тестовый пример выглядит следующим образом:

Результат:

------------- Scenario 1 - orElse() --------------------
1.1. Optional.isPresent() == true (Redundant call)
Going to a very far store to buy a new expensive phone
Used phone: MyCheapPhone

1.2. Optional.isPresent() == false
Going to a very far store to buy a new expensive phone
Used phone: NewExpensivePhone

------------- Scenario 2 - orElseGet() --------------------
2.1. Optional.isPresent() == true
Used phone: MyCheapPhone

2.2. Optional.isPresent() == false
Going to a very far store to buy a new expensive phone
Used phone: NewExpensivePhone

Код:

public class TestOptional {
public Optional<String> findMyPhone(int phoneId) {
return phoneId == 10
? Optional.of("MyCheapPhone")
: Optional.empty();
}

public String buyNewExpensivePhone() {
System.out.println("\tGoing to a very far store to buy a new expensive phone");
return "NewExpensivePhone";
}


public static void main(String[] args) {
TestOptional test = new TestOptional();
String phone;
System.out.println("------------- Scenario 1 - orElse() --------------------");
System.out.println(" 1.1. Optional.isPresent() == true (Redundant call)");
phone = test.findMyPhone(10).orElse(test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");

System.out.println(" 1.2. Optional.isPresent() == false");
phone = test.findMyPhone(-1).orElse(test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");

System.out.println("------------- Scenario 2 - orElseGet() --------------------");
System.out.println(" 2.1. Optional.isPresent() == true");
// Can be written as test::buyNewExpensivePhone
phone = test.findMyPhone(10).orElseGet(() -> test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");

System.out.println(" 2.2. Optional.isPresent() == false");
phone = test.findMyPhone(-1).orElseGet(() -> test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");
}
}
Ответ 2

Рассмотрим эти два сценария:

Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );

Если opt не содержит значения, то они действительно эквивалентны. Но если opt содержит значение, сколько Foo объектов будет создано?

P.s.: конечно, в этом примере разница, вероятно, не поддается измерению, но если вам нужно получить значение по умолчанию, например, из удаленного веб-сервиса или из базы данных, это внезапно становится очень важным.

Ответ 3

Я обратился сюда к проблеме, о которой упоминал Кудо.

Я делюсь своим опытом с другими.

orElse, или orElseGet, вот в чем вопрос:

static String B() {
System.out.println("B()...");
return "B";
}

public static void main(final String... args) {

System.out.println(
Optional.of("A").orElse(B()) // B()'s gonna be invoked anyway
);

System.out.println("----");

System.out.println(
Optional.of("A").orElseGet(() -> B()) // B() won't be invoked
);
}

С принтами

B()...
A
----
A

orElse вычисляет значение B() взаимозависимо от значения необязательного. Таким образом, orElseGet является ленивым.

Ответ 4

Я бы сказал, что самая большая разница между orElse и orElseGet возникает, когда мы хотим что-то оценить, чтобы получить новое значение в else условии.

Рассмотрим этот простой пример -

// oldValue is String type field that can be NULL
String value;
if (oldValue != null) {
value = oldValue;
} else {
value = apicall().value;
}

Теперь давайте преобразуем приведенный выше пример для использования Optional вместе с orElse,

// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);

Теперь давайте преобразуем приведенный выше пример для использования Optional вместе с orElseGet,

// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);

При вызове orElse apicall().value вычисляется и передается методу. Принимая во внимание, что в случае с orElseGet вычисление происходит только в том случае, если oldValue пустое значение. orElseGet допускает отложенную оценку.

java java-8