¿Cuál es el punto de la clase opcional de Guava?

89

Recientemente leí sobre esto y vi a personas que usaban esta clase, pero en casi todos los casos, el uso también nullhabría funcionado, si no de manera más intuitiva. ¿Alguien puede dar un ejemplo concreto donde Optionalse lograría algo que nullno se pudo o de una manera mucho más limpia? Lo único que se me ocurre es usarlo con Mapsque no acepte nullclaves, pero incluso eso se podría hacer con un "mapeo" lateral del valor nulo. ¿Alguien puede proporcionarme un argumento más convincente? Gracias.

RAYO
fuente
12
perorata aleatoria: odio cuando la gente usa en exceso un "patrón" y hace que el código se vea tan feo por algún beneficio teórico que no existe ...
RAY
A partir de Java8, ya no usaría esta clase Guava porque es menos poderosa que la de JDK. Ver stackoverflow.com/a/10756992/82609
Sebastien Lorber

Respuestas:

155

Miembro del equipo de guayaba aquí.

Probablemente la mayor desventaja nulles que no es obvio lo que debería significar en un contexto dado: no tiene un nombre ilustrativo. No siempre es obvio que nullsignifica "sin valor para este parámetro"; diablos, como valor de retorno, a veces significa "error", o incluso "éxito" (!!), o simplemente "la respuesta correcta no es nada". Optionales con frecuencia el concepto al que se refiere cuando convierte una variable en anulable, pero no siempre. Cuando no lo sea, le recomendamos que escriba su propia clase, similar a Optionalpero con un esquema de nomenclatura diferente, para dejar en claro lo que realmente quiere decir.

Pero yo diría que la mayor ventaja de Optionalno es la legibilidad: la ventaja es que es a prueba de idiotas. Te obliga a pensar activamente en el caso ausente si quieres que tu programa se compile, ya que tienes que desenvolver activamente Optionaly abordar ese caso. Null hace que sea inquietantemente fácil simplemente olvidar cosas, y aunque FindBugs ayuda, no creo que aborde el problema tan bien. Esto es especialmente relevante cuando devuelve valores que pueden estar "presentes" o no. Es mucho más probable que usted (y otros) olviden que other.method(a, b)podría devolver un nullvalor de lo que es probable que olvide que apodría ser nullcuando esté implementando other.method. RegresandoOptional hace imposible que las personas que llaman olviden ese caso, ya que tienen que desenvolver el objeto ellos mismos.

Por estas razones, le recomendamos que utilice Optionalcomo tipo de retorno para sus métodos, pero no necesariamente en los argumentos de su método.

(Por cierto, esto se basa totalmente en la discusión aquí ).

Louis Wasserman
fuente
3
+1 para una gran explicación. No sé si esta es la inspiración, pero agregaría un puntero a los tipos de opciones en SML, OCaml y F #, que tienen muchas de las mismas semánticas.
Adam Mihalcin
2
Siempre es bueno obtener una respuesta de alguien que estuvo directamente involucrado. Hubiera hecho +1 solo por eso. Haría +1 más por una gran respuesta. (pero no puedo). Creo que el punto de tenerlo como valor de retorno para obligar a los consumidores de un método desconocido a hacer un esfuerzo por reconocer que un "nada" puede devolverse es una razón de peso. Sin embargo, no me gusta cómo se usa en exceso (o incluso cómo se abusa). Por ejemplo, me siento realmente incómodo cuando veo que las personas ponen Opcionales como valores en Maps. El mapa de retorno nulo para una clave inexistente / sin asignar es un paradigma bien establecido ... No es necesario hacerlo más complicado ...
RAY
9
Está bien establecido que Mapregresa nullsi una clave no está asignada, pero recuerde que si lo hace map.put(key, null), map.containsKey(key)regresará truepero map.get(key)regresará null. Optionalpuede ser útil para aclarar la distinción entre el caso "asignado explícitamente a nulo" y el caso "no presente en el mapa". Concedo que Optionalse puede abusar de eso , pero aún no estoy convencido de que el caso que describe sea un abuso.
Louis Wasserman
8
Para usar una analogía anticuada, solía haber estas cosas gigantes llamadas "guías telefónicas" :-) y si le pidiera que buscara el número de alguien y dijera "no hay número para esa persona", diría "¿qué es decir, están ahí con un número que no figura en la lista o simplemente no pudiste encontrar una entrada para ellos ". Esas dos posibles respuestas se asignan muy bien a Optional.absent () y null respectivamente. Optional.absent () es el "positivo negativo" definitivo.
Kevin Bourrillion
3
@RAY: En cierto modo, ¿ Optional<T> es ese valor "encontrado, pero no válido"? O más precisamente, Optional<T>es una forma de decorar cualquier tipo Tcon un valor adicional "encontrado, pero no válido", creando un nuevo tipo combinando dos tipos existentes. Si tiene cien clases, puede resultar complicado tener que crear un valor "encontrado, pero no válido" para cada una, pero Optional<T>puede funcionar fácilmente para todas.
Daniel Pryden
9

Realmente se parece al Maybepatrón Monad de Haskell.

Debería leer lo siguiente, Wikipedia Monad (programación funcional) :

Y lea De Opcional a Mónada con guayaba en el Blog de Kerflyn, que trata sobre el Opcional de la guayaba utilizada como mónada:


Editar: Con Java8, hay un Opcional incorporado que tiene operadores monádicos como flatMap. Este ha sido un tema controvertido pero finalmente se ha implementado.

Ver http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html

public Optional<String> tryFindSimilar(String s)  //...

Optional<Optional<String>> bad = opt.map(this::tryFindSimilar);
Optional<String> similar =       opt.flatMap(this::tryFindSimilar);

El flatMapoperador es esencial para permitir operaciones monádicas y permite encadenar fácilmente llamadas que devuelven resultados opcionales.

Piénselo, si usara el mapoperador 5 veces terminaría con un Optional<Optional<Optional<Optional<Optional<String>>>>>, mientras que usarlo flatMaple daríaOptional<String>

Desde Java8, prefiero no usar el Opcional de Guava, que es menos poderoso.

Sebastien Lorber
fuente
6

Una buena razón para usarlo es que hace que sus valores nulos sean muy significativos. En lugar de devolver un nulo que podría significar muchas cosas (como error, falla, vacío, etc.), puede poner un 'nombre' a su nulo. Mira este ejemplo:

definamos un POJO básico:

class PersonDetails {

String person;
String comments;

public PersonDetails(String person, String comments) {
    this.person = person;
    this.comments = comments;
}

public String getPerson() {
    return person;
}


public String getComments() {
    return comments;
}

}

Ahora hagamos uso de este sencillo POJO:

public Optional<PersonDetails> getPersonDetailstWithOptional () {

  PersonDetails details = null; /*details of the person are empty but to the caller this is meaningless,
  lets make the return value more meaningful*/


    if (details == null) {
      //return an absent here, caller can check for absent to signify details are not present
        return Optional.absent();
    } else {
      //else return the details wrapped in a guava 'optional'
        return Optional.of(details);   
    }
}

Ahora evitemos el uso de nulo y hagamos nuestras comprobaciones con Opcional, por lo que es significativo

public void checkUsingOptional () {

    Optional<PersonDetails> details = getPersonDetailstWithOptional();

    /*below condition checks if persons details are present (notice we dont check if person details are null,
    we use something more meaningful. Guava optional forces this with the implementation)*/
    if (details.isPresent()) {

      PersonDetails details = details.get();

        // proceed with further processing
        logger.info(details);

    } else {
        // do nothing
        logger.info("object was null"); 
    }

    assertFalse(details.isPresent());
}

por lo tanto, al final, es una forma de hacer que los nulos sean significativos y menos ambiguos.

j2emanue
fuente
4

La ventaja más importante de Opcional es que agrega más detalles al contrato entre el implementador y el llamador de una función. Por esta razón, es útil tanto para los parámetros como para el tipo de retorno.

Si hace que la convención tenga siempre Optionalpara posibles objetos nulos, agrega más aclaraciones a casos como:

  1. Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)

    El contrato aquí especifica claramente que existe la posibilidad de que un resultado no se devuelva, pero también muestra que funcionará con fromy tocomo ausente.

  2. Optional<Integer> maxPrime(Optional<Integer> from, Integer to)

    El contrato especifica que from es opcional, por lo que un valor ausente puede tener un significado especial como comenzar desde 2. Puedo esperar que un valor nulo para el toparámetro arroje una excepción.

Entonces, lo bueno de usar Opcional es que el contrato se volvió tanto descriptivo (similar a la @NotNullanotación) como formal, ya que debe escribir código .get()para hacer frente Optional.

raisercostin
fuente
¿Por qué utilizar una <Lista> opcional cuando una lista vacía sería más limpia?
RAY
Elegí el ejemplo equivocado en la devolución. Con las colecciones, puede hacer que la convención devuelva siempre una colección vacía en lugar de un valor nulo. Si se usa esta convención, no necesita opcionales para las colecciones. Opcional podría percibirse como una colección con cero o un elemento, por lo que no es necesario colocarlo alrededor de otra colección.
raisercostin
2
según el contexto, podría haber una diferencia significativa entre una lista con 0 elementos y una lista ausente.
plasma147