Diferencia entre `Optional.orElse()` y `Optional.orElseGet()`

Resuelto jbx asked hace 9 años • 10 respuestas

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 NoSuchElementExceptiony isPresent()siempre devuelve verdadero... pero evidentemente no es así, simplemente devuelve Tcomo orElse()).

¿Hay alguna otra diferencia que me falta?

jbx avatar Oct 16 '15 19:10 jbx
Aceptado

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 elOptional.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");
    }
}
Hoa Nguyen avatar Dec 23 '2017 09:12 Hoa Nguyen

Considere estos dos escenarios:

Optional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );

Si optno contiene un valor, los dos son equivalentes. Pero si opt contiene un valor, ¿cuántos Fooobjetos 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.

biziclop avatar Oct 16 '2015 12:10 biziclop