J'essaie de comprendre la différence entre les méthodes Optional<T>.orElse()
et Optional<T>.orElseGet()
.
La description de la méthode orElse()
est "Renvoie la valeur, le cas échéant, sinon renvoie autre."
Alors que, la description de la méthode orElseGet()
est "Renvoie la valeur, le cas échéant, sinon invoquez autre et renvoie le résultat de cette invocation."
La méthode orElseGet()
utilise une interface fonctionnelle fournisseur, qui ne prend généralement aucun paramètre et renvoie T
.
Dans quelle situation auriez-vous besoin d'utiliser orElseGet()
? Si vous avez une méthode T myDefault()
, pourquoi ne pas simplement faire optional.orElse(myDefault())
plutôt que optional.orElseGet(() -> myDefault())
?
Il ne semble pas que orElseGet()
reporte l'exécution de l'expression lambda à une date ultérieure, alors quel est le but de celle-ci? (J'aurais pensé qu'il serait plus utile de renvoyer un Optional<T>
plus sûr dont get()
ne jette jamais un NoSuchElementException
et isPresent()
toujours vrai ... mais évidemment non, il retourne simplement T
comme orElse()
).
Y a-t-il une autre différence qui me manque?
Prenons ces deux scénarios:
Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );
Si opt
ne contient pas de valeur, les deux sont effectivement équivalents. Mais si opt
est-ce que contient une valeur, combien d'objets Foo
seront créés?
P.s .: bien sûr, dans cet exemple, la différence ne serait probablement pas mesurable, mais si vous devez obtenir votre valeur par défaut d'un service Web distant, par exemple, ou d'une base de données, elle devient soudainement très importante.
Je suis arrivé ici pour le problème Kudo mentionné.
Je partage mon expérience pour les autres.
orElse
, ou orElseGet
, telle est la question: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()));
}
empreintes
B()...
A
A
Réponse courte:
Optional.isPresent()
Optional.isPresent() == false
En code réel, vous pouvez envisager la seconde approche lorsque la ressource requise est coûteuse à obtenir .
// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource());
// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource())
Pour plus de détails, considérons l'exemple suivant avec cette fonction:
public Optional<String> findMyPhone(int phoneId)
La différence est comme ci-dessous:
X : buyNewExpensivePhone() called
+——————————————————————————————————————————————————————————————————+——————————————+
| Optional.isPresent() | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone()) | X | X |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) | | X |
+——————————————————————————————————————————————————————————————————+——————————————+
Lorsque optional.isPresent() == false
, il n'y a pas de différence entre deux façons. Cependant, lorsque optional.isPresent() == true
, orElse()
appelle toujours la fonction suivante, que vous le vouliez ou non.
Enfin, le cas de test utilisé est le suivant:
Résultat:
------------- 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");
}
}
Je dirais que la plus grande différence entre orElse
et orElseGet
survient lorsque nous voulons évaluer quelque chose pour obtenir la nouvelle valeur dans la condition else
.
Considérez cet exemple simple -
// oldValue is String type field that can be NULL
String value;
if (oldValue != null) {
value = oldValue;
} else {
value = apicall().value;
}
Maintenant transformons l'exemple ci-dessus en utilisant Optional
avec orElse
,
// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);
Maintenant transformons l'exemple ci-dessus en utilisant Optional
avec orElseGet
,
// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);
Lorsque orElse
est appelé, la fonction apicall().value
est évaluée et transmise à la méthode. Alors que, dans le cas de orElseGet
, l'évaluation n'a lieu que si oldValue
est vide. orElseGet
permet une évaluation paresseuse.
L'exemple suivant devrait démontrer la différence:
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());
La réponse apparaît également dans la documentation.
public T orElseGet(Supplier<? extends T> other)
:
Renvoie la valeur si elle est présente, sinon invoke other et renvoie le résultat de cet appel.
La Supplier
ne sera pas être invoquée si la Optional
présente. tandis que,
Retourne la valeur si présente, sinon retourne autre.
Si other
est une méthode qui renvoie une chaîne, elle sera invoquée, mais sa valeur ne sera pas renvoyée si la variable Optional
existe.
La différence est assez subtile et si vous ne payez pas beaucoup d'attention, vous continuerez à l'utiliser de manière erronée.
Le meilleur moyen de comprendre la différence entre orElse()
et orElseGet()
est que orElse()
sera toujours exécuté si le Optional<T>
est null ou not, Mais orElseGet()
ne sera exécuté que lorsque Optional<T>
sera null.
La signification du dictionnaire ouElse est : - exécute la partie quand quelque chose n'est pas présent, mais ici, cela contredit, voir l'exemple ci-dessous:
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 n'est pas NULL, je suis toujours en cours d'exécution
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";
}
Sortie: emptyOptional est NULL, je suis en cours d'exécution, c'est normal selon le dictionnaire
Pour
orElseGet()
, la méthode est conforme à la signification du dictionnaire. La partieorElseGet()
ne sera exécutée que si Optional est null.
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 |
+--------------------+------+-----+------------+-------------+-------+
Remarques:
orElseGet()
a nettement surperforméorElse()
pour notre exemple particulier.
J'espère que ça dissipe les doutes des gens comme moi qui veulent un exemple de base:)
Considérant le code suivant:
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();
}
}
si nous obtenons value
de cette manière: Optional.<String>ofNullable(null)
, il n'y a pas de différence entre orElseGet () et orElse (), mais si nous obtenons value
de cette façon: Optional.<String>ofNullable("test")
, orelesMethod()
dans orElseGet()
ne sera pas appelé mais dans orElse()