java - baeldung - Diferencia entre `Opcional.oElse()` y `Opcional.oElseGet()`
optional java ejemplo (7)
Estoy tratando de entender la diferencia entre los métodos
Optional<T>.orElse()
y
Optional<T>.orElseGet()
.
La descripción del método
orElse()
es
"Devuelve el valor si está presente, de lo contrario devuelve otro".
Mientras que la descripción del método
orElseGet()
es
"Devuelve el valor si está presente, de lo contrario invoca a otro y devuelve el resultado de esa invocación"
El método
orElseGet()
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 tiene un método
T myDefault()
¿por qué no haría
optional.orElse(myDefault())
lugar de
optional.orElseGet(() -> myDefault())
?
No parece que
orElseGet()
la ejecución de la expresión lambda en algún momento posterior o algo así, ¿cuál es el punto?
(Pensé que sería más útil si devolviera un
Optional<T>
más seguro cuyo
get()
nunca arroja una
NoSuchElementException
y
isPresent()
siempre devuelve verdadero ... pero evidentemente no lo es, solo devuelve
T
como o
orElse()
).
¿Hay alguna otra diferencia que me estoy perdiendo?
Diría que la mayor diferencia entre
orElse
y
orElseGet
produce cuando queremos evaluar algo para obtener el nuevo valor en la condición
else
.
Considere este simple ejemplo:
// oldValue is String type field that can be NULL
String value;
if (oldValue != null) {
value = oldValue;
} else {
value = apicall().value;
}
Ahora transformemos el ejemplo anterior para usar
Optional
junto con
orElse
,
// oldValue is Optional type field
String value = oldValue.orElse(apicall().value);
Ahora transformemos el ejemplo anterior para usar
Optional
junto con
orElseGet
,
// oldValue is Optional type field
String value = oldValue.orElseGet(() -> apicall().value);
Cuando se invoca
orElse
, el valor
apicall().value
Se evalúa y se pasa al método.
Mientras que, en el caso de
orElseGet
la evaluación solo ocurre si
oldValue
está vacío.
orElseGet
permite una evaluación perezosa.
El siguiente ejemplo debería demostrar la diferencia:
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 respuesta también aparece en los documentos.
Devuelva el valor si está presente, de lo contrario invoque a otro y devuelva el resultado de esa invocación.
No
se invocará al
Supplier
si se presenta el
Optional
.
mientras,
Devuelve el valor si está presente, de lo contrario devuelve otro
Si
other
es un método que devuelve una cadena, se invocará, pero su valor no se devolverá en caso de que el
Optional
exista.
La diferencia es bastante sutil y si no prestas mucha atención, la seguirás usando de manera incorrecta.
La mejor manera de entender la diferencia entre
orElse()
y
orElseGet()
es que
orElse()
siempre se ejecutará si el
Optional<T>
es
nulo
o
no
, pero
orElseGet()
solo se ejecutará cuando el
Optional<T>
sea
nulo
.
El significado del diccionario de orElse es : - ejecuta la parte cuando algo no está presente, pero aquí contradice, mira el siguiente ejemplo:
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());
Salida: nonEmptyOptional no es NULL, todavía estoy siendo ejecutado
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();
}
}
Salida : emptyOptional es NULL, estoy en ejecución, es normal según el diccionario
Para
orElseGet()
, el método funciona según el significado del diccionario, la parteorElseGet()
se ejecutará solo cuando el Opcional sea nulo .
Puntos de referencia :
+--------------------+------+-----+------------+-------------+-------+ | Benchmark | Mode | Cnt | Score | Error | Units | +--------------------+------+-----+------------+-------------+-------+ | orElseBenchmark | avgt | 20 | 60934.425 | ± 15115.599 | ns/op | +--------------------+------+-----+------------+-------------+-------+ | orElseGetBenchmark | avgt | 20 | 3.798 | ± 0.030 | ns/op | +--------------------+------+-----+------------+-------------+-------+
Observaciones :
orElseGet()
ha superado claramente aorElse()
en nuestro ejemplo particular.
Espero que aclare las dudas de personas como yo que quieren el ejemplo básico muy básico :)
Llegué aquí por el problema que Kudo mencionó.
Estoy compartiendo mi experiencia por los demás.
o
orElse
, o
orElseGet
, esa es la pregunta:
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()));
}
huellas dactilares
B()...
A
A
orElse
evalúa el valor de B () de manera interdependiente del valor de lo opcional.
Por lo tanto,
orElseGet
es vago.
Respuesta corta:
-
orElse ()
siempre llamará a la función dada, lo desee o no, independientemente del valor de
Optional.isPresent()
-
orElseGet ()
solo llamará a la función dada cuando el
Optional.isPresent() == false
En el código real, es posible que desee considerar el segundo enfoque cuando el recurso requerido es costoso de obtener .
// 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
, o
orElse()
siempre llama a la función posterior, lo desee o no.
Finalmente, el caso de prueba utilizado es el siguiente:
Resultado:
------------- 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
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");
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");
}
}
Teniendo en cuenta el siguiente código:
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 obtenemos
value
de esta manera:
Optional.<String>ofNullable(null)
, no hay diferencia entre orElseGet () y orElse (), pero si obtenemos
value
de esta manera:
Optional.<String>ofNullable("test")
,
orelesMethod()
en
orElseGet()
no se llamará pero en
orElse()
se llamará
Tome 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 objetos
Foo
se crearán?
Ps: por supuesto, en este ejemplo, la diferencia probablemente no sería medible, 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.