Ich versuche den Unterschied zwischen den Methoden Optional<T>.orElse()
und Optional<T>.orElseGet()
zu verstehen.
Die Beschreibung für die orElse()
-Methode lautet "Gibt den Wert zurück, falls vorhanden, andernfalls einen anderen."
Die Beschreibung für die orElseGet()
-Methode lautet zwar "Gibt den Wert zurück, falls vorhanden. Andernfalls rufen Sie other auf und geben das Ergebnis dieses Aufrufs zurück."
Die orElseGet()
-Methode verwendet eine Supplier-Funktionsschnittstelle, die im Wesentlichen keine Parameter übernimmt und T
zurückgibt.
In welcher Situation müssten Sie orElseGet()
verwenden? Wenn Sie eine Methode T myDefault()
haben, warum würden Sie nicht optional.orElse(myDefault())
statt optional.orElseGet(() -> myDefault())
tun?
Es scheint nicht so, als würde orElseGet()
die Ausführung des Lambda-Ausdrucks auf einen späteren Zeitpunkt oder etwas verschieben, was ist der Sinn davon? (Ich hätte gedacht, dass es sinnvoller wäre, wenn es einen sichereren Optional<T>
zurückgibt, dessen get()
niemals eine NoSuchElementException
auslöst und isPresent()
immer true zurückgibt ... aber offensichtlich nicht, es gibt nur T
wie orElse()
zurück).
Gibt es einen anderen Unterschied, den ich vermisse?
Nehmen Sie diese zwei Szenarien:
Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );
Wenn opt
keinen Wert enthält, sind die beiden tatsächlich gleichwertig. Wenn opt
enthält einen Wert enthält, wie viele Foo
-Objekte werden erstellt?
P.s .: In diesem Beispiel wäre der Unterschied wahrscheinlich nicht messbar, aber wenn Sie Ihren Standardwert beispielsweise von einem Remote-Webdienst oder von einer Datenbank erhalten müssen, wird dies plötzlich sehr wichtig.
Ich griff hier nach dem Problem Kudo an.
Ich teile meine Erfahrung für andere.
orElse
oder orElseGet
, das ist die Frage: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()));
}
druckt
B()...
A
A
Kurze Antwort:
Optional.isPresent()
-Wert immer aufOptional.isPresent() == false
In echtem Code sollten Sie den zweiten Ansatz in Betracht ziehen, wenn die benötigte Ressource teuer ist zu bekommen.
// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource());
// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource())
Betrachten Sie für weitere Details das folgende Beispiel mit dieser Funktion:
public Optional<String> findMyPhone(int phoneId)
Der Unterschied ist wie folgt:
X : buyNewExpensivePhone() called
+——————————————————————————————————————————————————————————————————+——————————————+
| Optional.isPresent() | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone()) | X | X |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) | | X |
+——————————————————————————————————————————————————————————————————+——————————————+
Bei optional.isPresent() == false
gibt es keinen Unterschied zwischen zwei Möglichkeiten. Wenn jedoch optional.isPresent() == true
aufgerufen wird, ruft orElse()
die nachfolgende Funktion immer auf, ob Sie dies wünschen oder nicht.
Schließlich wird der Testfall wie folgt verwendet:
Ergebnis:
------------- 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
Code:
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");
}
}
Ich würde sagen, der größte Unterschied zwischen orElse
und orElseGet
besteht, wenn wir etwas auswerten wollen, um den neuen Wert in der else
-Bedingung zu erhalten.
Betrachten Sie dieses einfache Beispiel -
// oldValue is String type field that can be NULL
String value;
if (oldValue != null) {
value = oldValue;
} else {
value = apicall().value;
}
Lassen Sie uns nun das obige Beispiel in Optional
zusammen mit orElse
umwandeln.
// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);
Lassen Sie uns nun das obige Beispiel in Optional
zusammen mit orElseGet
umwandeln.
// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);
Wenn orElse
aufgerufen wird, wird die apicall().value
ausgewertet und an die Methode übergeben. Bei orElseGet
erfolgt die Auswertung jedoch nur, wenn die oldValue
leer ist. orElseGet
ermöglicht eine faule Auswertung.
Das folgende Beispiel soll den Unterschied verdeutlichen:
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());
Die Antwort wird auch in den Dokumenten angezeigt.
public T orElseGet(Supplier<? extends T> other)
:
Gibt den Wert zurück, falls vorhanden, andernfalls invoke other und gibt das Ergebnis dieses Aufrufs zurück.
Die Supplier
wird nicht wird aufgerufen, wenn die Optional
vorhanden ist. wohingegen,
Geben Sie den Wert zurück, falls vorhanden, andernfalls geben Sie other zurück.
Wenn other
eine Methode ist, die eine Zeichenfolge zurückgibt, wird sie aufgerufen, ihr Wert wird jedoch nicht zurückgegeben, falls die Optional
vorhanden ist.
Der Unterschied ist ziemlich subtil und wenn Sie nicht viel Aufmerksamkeit schenken, werden Sie es in einer falschen Weise verwenden.
Der beste Weg, um den Unterschied zwischen orElse()
und orElseGet()
zu verstehen, ist, dass orElse()
immer ausgeführt wird, wenn Optional<T>
null oder nicht, aber orElseGet()
wird nur ausgeführt, wenn Optional<T>
null ist.
Die Wörterbuchbedeutung von orElse ist : - führen Sie den Teil aus, wenn etwas nicht vorhanden ist, aber hier widerspricht es, siehe das folgende Beispiel:
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";
}
Output: nonEmptyOptional ist nicht NULL, dennoch werde ich ausgeführt
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";
}
Ausgabe: emptyOptional ist NULL, ich werde ausgeführt, es ist normal wie im Wörterbuch
Für
orElseGet()
wird die Methode gemäß der Wörterbuchbedeutung ausgeführt. Der TeilorElseGet()
wird nur ausgeführt, wenn Optional null ist.
Benchmarks:
+--------------------+------+-----+------------+-------------+-------+
| Benchmark | Mode | Cnt | Score | Error | Units |
+--------------------+------+-----+------------+-------------+-------+
| orElseBenchmark | avgt | 20 | 60934.425 | ± 15115.599 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
| orElseGetBenchmark | avgt | 20 | 3.798 | ± 0.030 | ns/op |
+--------------------+------+-----+------------+-------------+-------+
Bemerkungen:
orElseGet()
hatorElse()
für unser spezielles Beispiel deutlich übertroffen.
Hoffe, es klärt die Zweifel von Leuten wie mir, die das grundlegende Beispiel wollen:)
Betrachten Sie den folgenden Code:
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();
}
}
wenn wir value
auf diese Weise erhalten: Optional.<String>ofNullable(null)
, besteht kein Unterschied zwischen orElseGet () und orElse (), aber wenn wir value
auf diese Weise erhalten: Optional.<String>ofNullable("test")
wird orelesMethod()
in orElseGet()
nicht aufgerufen, aber in orElse()
aufgerufen