Разница между `Optional.orElse()` и `Optional.orElseGet()`
Я пытаюсь понять разницу между методами Optional<T>.orElse()
и Optional<T>.orElseGet()
.
Описание метода orElse()
: "Вернуть значение, если оно присутствует, иначе вернуть другое".
В то время как описание метода orElseGet()
- "Возвратить значение, если оно присутствует, иначе вызвать другое и вернуть результат этого вызова".
Метод orElseGet()
использует функциональный интерфейс поставщика, который по существу не принимает никаких параметров и возвращает T
.
В какой ситуации вам понадобится использовать orElseGet()
? Если у вас есть метод T myDefault()
, почему бы вам просто не сделать optional.orElse(myDefault())
, а не optional.orElseGet(() -> myDefault())
?
Кажется, что orElseGet()
откладывает выполнение выражения лямбды до какого-то более позднего времени или что-то в этом роде, и что это за точка? (Я бы подумал, что было бы более полезно, если бы он вернул более безопасный Optional<T>
, который get()
никогда не выбрасывает NoSuchElementException
и isPresent()
всегда возвращает true... но, очевидно, его нет, он просто возвращает T
например orElse()
).
Есть ли какая-то другая разница, которую мне не хватает?
Ответы
Ответ 1
Возьмите эти два сценария:
Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );
Если opt
не содержит значения, они действительно эквивалентны. Но если opt
содержит значение, сколько объектов Foo
будет создано?
Ps: конечно, в этом примере разница, вероятно, не будет измерима, но если вам нужно получить значение по умолчанию из удаленного веб-сервиса, например, или из базы данных, это внезапно становится очень важным.
Ответ 2
Короткий ответ:
- 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
, между двумя способами нет никакой разницы. Однако, когда orElse()
optional.isPresent() == true
, orElse()
всегда вызывает последующую функцию, хотите вы этого или нет.
И наконец, используемый тестовый пример, как показано ниже:
Результат:
------------- Scenario 1 - orElse() --------------------
1.1. Optional.isPresent() == true
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");
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");
}
}
Ответ 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()));
System.out.println(Optional.of("A").orElseGet(() -> B()));
}
печать
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
позволяет ленивую оценку.
Ответ 5
Разница довольно тонкая, и если вы не будете обращать особого внимания, вы будете использовать ее неправильно.
Лучший способ понять разницу между orElse()
и orElseGet()
состоит в том, что orElse()
всегда будет выполняться, если Optional<T>
равен нулю или не, но orElseGet()
будет выполняется только тогда, когда Optional<T>
равен нулю.
В словаре значение orElse равно : - выполнять деталь, когда чего-то нет, но здесь это противоречит, см. пример ниже:
Optional<String> nonEmptyOptional = Optional.of("Vishwa Ratna");
String value = nonEmptyOptional.orElse(iAmStillExecuted());
public static String iAmStillExecuted(){
System.out.println("nonEmptyOptional is not NULL,still I am being executed");
return "I got executed";
}
Вывод: nonEmptyOptional не равен NULL, но я все еще выполняюсь
Optional<String> emptyOptional = Optional.ofNullable(null);
String value = emptyOptional.orElse(iAmStillExecuted());
public static String iAmStillExecuted(){
System.out.println("emptyOptional is NULL, I am being executed, it is normal as
per dictionary");
return "I got executed";
}
Выходные данные: emptyOptional НЕДЕЙСТВИТЕЛЕН, я выполняюсь, это нормально согласно словарь
Для orElseGet()
, метод идет в соответствии со значением словаря, Партия orElseGet()
будет выполняться только когда опционально нулевой.
Контрольные показатели:
+--------------------+------+-----+------------+-------------+-------+
| Benchmark | Mode | Cnt | Score | Error | Units |
+--------------------+------+-----+------------+-------------+-------+
| orElseBenchmark | avgt | 20 | 60934.425 | ± 15115.599 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
| orElseGetBenchmark | avgt | 20 | 3.798 | ± 0.030 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
Замечания: orElseGet()
явно превзошел orElse()
для нашего конкретного примера.
Надеюсь, это прояснит сомнения таких людей, как я, которые хотят получить самый простой пример из жизни :)
Ответ 6
Следующий пример должен продемонстрировать разницу:
String destroyTheWorld() {
// destroy the world logic
return "successfully destroyed the world";
}
Optional<String> opt = Optional.empty();
// we're dead
opt.orElse(destroyTheWorld());
// we're safe
opt.orElseGet(() -> destroyTheWorld());
Ответ также появляется в документах.
public T orElseGet(Supplier<? extends T> other)
:
Верните значение, если оно присутствует, в противном случае вызовите other и верните результат этого вызова.
Supplier
не будет вызван, если Optional
подарки. в то время как,
public T orElse(T other)
:
Вернуть значение, если оно присутствует, в противном случае вернуть другое.
Если other
- это метод, который возвращает строку, она будет вызвана, но это значение не будет возвращено в случае, если Optional
существует.
Ответ 7
Учитывая следующий код:
import java.util.Optional;
// one class needs to have a main() method
public class Test
{
public String orelesMethod() {
System.out.println("in the Method");
return "hello";
}
public void test() {
String value;
value = Optional.<String>ofNullable("test").orElseGet(this::orelesMethod);
System.out.println(value);
value = Optional.<String>ofNullable("test").orElse(orelesMethod());
System.out.println(value);
}
// arguments are passed using the text field below this editor
public static void main(String[] args)
{
Test test = new Test();
test.test();
}
}
если мы получим value
следующим образом: Optional.<String>ofNullable(null)
, нет разницы между orElseGet() и orElse(), но если мы получим value
таким образом: Optional.<String>ofNullable("test")
, orelesMethod()
в orElseGet()
не будет вызываться, но в orElse()
он будет вызываться