Qual é o objetivo da aula opcional de Guava

89

Recentemente li sobre isso e vi pessoas usando essa classe, mas em quase todos os casos, usar nulltambém funcionaria - se não de forma mais intuitiva. Alguém pode dar um exemplo concreto de onde Optionalconseguiria algo que nullnão poderia ou de uma forma muito mais limpa? A única coisa que posso pensar é em usá-lo com chaves Mapsque não aceitam null, mas mesmo isso poderia ser feito com um "mapeamento" lateral do valor de null. Alguém pode me fornecer um argumento mais convincente? Obrigado.

RAIO
fonte
12
discurso aleatório: eu odeio quando as pessoas abusam de um "padrão" e fazem o código parecer tão feio para algum benefício teórico que não existe ...
RAIO
A partir do Java8, eu não usaria mais essa classe Guava porque ela é menos poderosa que a do JDK. Consulte stackoverflow.com/a/10756992/82609
Sebastien Lorber

Respostas:

155

Membro da equipe Guava aqui.

Provavelmente, a maior desvantagem de nullé que não é óbvio o que deveria significar em qualquer contexto: ele não tem um nome ilustrativo. Nem sempre é óbvio que nullsignifica "nenhum valor para este parâmetro" - diabos, como um valor de retorno, às vezes significa "erro", ou mesmo "sucesso" (!!), ou simplesmente "a resposta correta não é nada". Optionalé frequentemente o conceito que você realmente quer dizer quando torna uma variável anulável, mas nem sempre. Quando não for, recomendamos que você escreva sua própria classe, semelhante a, Optionalmas com um esquema de nomenclatura diferente, para deixar claro o que você realmente quer dizer.

Mas eu diria que a maior vantagem de Optionalnão está na legibilidade: a vantagem é sua prova de idiota. Ele força você a pensar ativamente sobre o caso ausente se você deseja que seu programa compile, já que você tem que desembrulhar ativamente o Optionale resolver esse caso. Null torna perturbadoramente fácil simplesmente esquecer as coisas e, embora FindBugs ajude, não acho que resolva o problema tão bem. Isso é especialmente relevante quando você retorna valores que podem ou não estar "presentes". Você (e outros) são muito mais propensos a esquecer que other.method(a, b)poderia retornar um nullvalor do que é provável que você se esqueça de que apoderia ser nullquando você está implementando other.method. RetornandoOptional torna impossível para os chamadores esquecerem esse caso, uma vez que eles próprios precisam desembrulhar o objeto.

Por esses motivos, recomendamos que você use Optionalcomo um tipo de retorno para seus métodos, mas não necessariamente em seus argumentos de método.

(A propósito, isso é totalmente baseado na discussão aqui ).

Louis Wasserman
fonte
3
1 para uma ótima explicação. Não sei se essa é a inspiração, mas gostaria de adicionar um ponteiro para os tipos de opção em SML, OCaml e F #, que têm muitas da mesma semântica.
Adam Mihalcin
2
É sempre bom receber uma resposta de alguém que estava diretamente envolvido. Eu teria marcado com +1 apenas por isso. Seria +1 mais para uma ótima resposta. (mas não posso.) Acho que o ponto de tê-lo como um valor de retorno para forçar os consumidores de um método desconhecido a fazer um esforço para reconhecer que um "nada" pode ser devolvido é um motivo convincente. Eu não gosto de como isso está sendo usado demais (ou mesmo abusado). por exemplo, me sinto muito desconfortável ao ver as pessoas colocando Opcionais como valores no Maps. O mapa retendo nulo para uma chave inexistente / não mapeada é um paradigma bem estabelecido ... Não há necessidade de torná-lo mais complicado ...
RAY
9
É bem estabelecido que Mapretorna nullse uma chave não estiver mapeada, mas lembre-se de que se você fizer isso map.put(key, null), map.containsKey(key)retornará, truemas map.get(key)retornará null. Optionalpode ser útil para esclarecer a distinção entre o caso "explicitamente mapeado para nulo" e o caso "não presente no mapa". Admito que Optionalpode haver abuso, mas ainda não estou convencido de que o caso que você descreve seja um abuso.
Louis Wasserman
8
Para usar uma analogia antiquada, costumava haver essas coisas gigantes chamadas "listas telefônicas" :-) e se eu pedisse para você procurar o número de alguém e você dissesse "não há número para essa pessoa", eu diria "o que você faz quer dizer, eles estão lá com um número não listado ou você simplesmente não conseguiu encontrar uma entrada para eles? " Essas duas respostas possíveis mapeiam bem para Optional.absent () e null, respectivamente. Optional.absent () é o "negativo positivo" definitivo.
Kevin Bourrillion
3
@RAY: De certa forma, Optional<T> é esse valor "encontrado, mas não válido". Ou, mais precisamente, Optional<T>é uma maneira de decorar qualquer tipo Tcom um valor extra "encontrado, mas não válido" - criando um novo tipo combinando dois tipos existentes. Se você tiver cem classes, pode ficar complicado ter que criar um valor "encontrado, mas não válido" separado para cada uma, mas Optional<T>pode funcionar facilmente para todas elas.
Daniel Pryden
9

Realmente se parece com o Maybepadrão Monad de Haskell.

Você deve ler o seguinte, Wikipedia Monad (programação funcional) :

E leia Do Opcional à Mônada com Goiaba no Blog de Kerflyn, que discute sobre o Opcional da Goiaba usada como Mônada:


Edit: Com Java8, há um opcional embutido que possui operadores monádicos como flatMap. Este tem sido um assunto controverso, mas finalmente foi implementado.

Consulte 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);

O flatMapoperador é essencial para permitir operações monádicas e permite encadear facilmente chamadas que retornem resultados opcionais.

Pense nisso, se você usasse o mapoperador 5 vezes, acabaria com um Optional<Optional<Optional<Optional<Optional<String>>>>>, enquanto o uso flatMapdaria a vocêOptional<String>

Desde Java8 eu prefiro não usar Guava's Optional que é menos poderoso.

Sebastien Lorber
fonte
6

Um bom motivo para usá-lo é que torna seus valores nulos muito significativos. Em vez de retornar um nulo que pode significar muitas coisas (como erro, falha ou vazio, etc), você pode colocar um 'nome' em seu nulo. Veja este exemplo:

vamos definir um 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;
}

}

Agora vamos usar este POJO simples:

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);   
    }
}

Agora vamos evitar o uso de nulo e fazer nossas verificações com Opcional para que seja 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());
}

portanto, no final, é uma maneira de tornar os nulos significativos e menos ambigüos.

j2emanue
fonte
4

A vantagem mais importante de Optional é que ele adiciona mais detalhes ao contrato entre o implementador e o responsável pela chamada de uma função. Por esse motivo, é útil para parâmetros e tipo de retorno.

Se você fizer a convenção de sempre ter Optionalpara possíveis objetos nulos, você adicionará mais esclarecimentos a casos como:

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

    O contrato aqui especifica claramente que há uma chance de um resultado não ser retornado, mas também mostra que funcionará com frome tocomo ausente.

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

    O contrato especifica que de é opcional, portanto, um valor ausente pode ter um significado especial, como começar de 2. Posso esperar que um valor nulo para o toparâmetro lance uma exceção.

Portanto, a parte boa de usar Optional é que o contrato se tornou descritivo (semelhante à @NotNullanotação), mas também formal, uma vez que você deve escrever código .get()para lidar com ele Optional.

raisercostin
fonte
Por que usar uma <Lista> opcional quando uma lista vazia seria mais limpa?
RAY
Escolhi o exemplo errado no retorno. Com as coleções, você pode fazer a convenção de sempre retornar uma coleção vazia em vez de um valor nulo. Se esta convenção for usada, você não precisa de opcionais para coleções. Opcional pode ser percebido como uma coleção com zero ou um elemento, portanto, não é necessário colocá-lo em torno de outra coleção.
raisercostin
2
dependendo do contexto, pode haver uma diferença significativa entre uma lista com 0 itens e uma lista ausente.
plasma147 de