Разница между `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
допускает отложенную оценку.