Diferencia entre `Optional.orElse()` y `Optional.orElseGet()`
Estoy tratando de entender la diferencia entre los métodos Optional<T>.orElse()
y Optional<T>.orElseGet()
.
La descripción del orElse()
método es "Devuelve el valor si está presente; de lo contrario, devuelve otro".
Mientras que la descripción del orElseGet()
método es "Devolver el valor si está presente; de lo contrario, invoca otro y devuelve el resultado de esa invocación".
El orElseGet()
método toma una interfaz funcional de Proveedor, que esencialmente no toma ningún parámetro y devuelve T
.
¿En qué situación necesitarías usar orElseGet()
? Si tienes un método, T myDefault()
¿por qué no lo harías optional.orElse(myDefault())
en lugar de hacerlo optional.orElseGet(() -> myDefault())
?
No parece que orElseGet()
esté posponiendo la ejecución de la expresión lambda para un momento posterior o algo así, entonces, ¿cuál es el punto? (Habría pensado que sería más útil si devolviera un valor más seguro Optional<T>
cuyo get()
nunca arroja a NoSuchElementException
y isPresent()
siempre devuelve verdadero... pero evidentemente no es así, simplemente devuelve T
como orElse()
).
¿Hay alguna otra diferencia que me falta?
Respuesta corta:
- orElse() siempre llamará a la función dada, lo desees o no, independientemente del
Optional.isPresent()
valor - orElseGet() sólo llamará a la función dada cuando el
Optional.isPresent() == false
En código real, es posible que desee considerar el segundo enfoque cuando es costoso obtener el recurso requerido .
// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource());
// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource())
Para más detalles, considere el siguiente ejemplo con esta función:
public Optional<String> findMyPhone(int phoneId)
La diferencia es la siguiente:
X : buyNewExpensivePhone() called
+——————————————————————————————————————————————————————————————————+——————————————+
| Optional.isPresent() | true | false |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone()) | X | X |
+——————————————————————————————————————————————————————————————————+——————————————+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) | | X |
+——————————————————————————————————————————————————————————————————+——————————————+
Cuando optional.isPresent() == false
, no hay diferencia entre dos formas. Sin embargo, cuando optional.isPresent() == true
, orElse()
siempre llama a la función siguiente, lo desee o no.
Finalmente, el caso de prueba utilizado es el siguiente:
Resultado:
------------- 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
Código:
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");
}
}
Considere estos dos escenarios:
Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );
Si opt
no contiene un valor, los dos son equivalentes. Pero si opt
contiene un valor, ¿cuántos Foo
objetos se crearán?
Ps: por supuesto, en este ejemplo la diferencia probablemente no sería mensurable, pero si tiene que obtener su valor predeterminado de un servicio web remoto, por ejemplo, o de una base de datos, de repente se vuelve muy importante.