Quel est l'intérêt de la classe facultative de Guava

89

J'ai récemment lu à ce sujet et vu des gens utiliser cette classe, mais dans presque tous les cas, l'utilisation nullaurait également fonctionné - sinon plus intuitivement. Quelqu'un peut-il donner un exemple concret où Optionalréaliserait quelque chose qui nullne pourrait pas ou d'une manière beaucoup plus propre? La seule chose à laquelle je peux penser est de l'utiliser avec Mapsqui n'accepte pas les nullclés, mais même cela pourrait être fait avec un côté "mapping" de la valeur de null. Quelqu'un peut-il me fournir un argument plus convaincant? Je vous remercie.

RAYON
la source
12
coup de gueule aléatoire: je déteste quand les gens abusent d'un «modèle» et donnent au code un aspect si laid pour un avantage théorique qui n'existe pas ...
RAY
À partir de Java8, je n'utiliserais plus cette classe Guava car elle est moins puissante que celle du JDK. Voir stackoverflow.com/a/10756992/82609
Sebastien Lorber

Réponses:

155

Membre de l'équipe Guava ici.

Le plus gros inconvénient nullest probablement qu'il n'est pas évident de savoir ce que cela devrait signifier dans un contexte donné: il n'a pas de nom illustratif. Ce n'est pas toujours évident que cela nullsignifie «aucune valeur pour ce paramètre» - diable, comme valeur de retour, parfois cela signifie «erreur», ou même «succès» (!!), ou simplement «la bonne réponse n'est rien». Optionalest souvent le concept que vous entendez réellement lorsque vous rendez une variable Nullable, mais pas toujours. Si ce n'est pas le cas, nous vous recommandons d'écrire votre propre classe, similaire Optionalmais avec un schéma de dénomination différent, pour clarifier ce que vous entendez réellement.

Mais je dirais que le plus grand avantage de Optionaln'est pas dans la lisibilité: l'avantage est sa preuve d'idiot. Cela vous oblige à réfléchir activement au cas absent si vous voulez que votre programme se compile du tout, car vous devez activement déballer le cas Optionalet résoudre ce cas. Null permet d'oublier facilement des choses, et bien que FindBugs aide, je ne pense pas qu'il résout le problème aussi bien. Ceci est particulièrement pertinent lorsque vous renvoyez des valeurs qui peuvent ou non être "présentes". Vous (et d'autres) êtes beaucoup plus susceptibles d'oublier que cela other.method(a, b)pourrait renvoyer une nullvaleur que vous ne l'oublierez aprobablement nulllors de la mise en œuvre other.method. RetourOptional rend impossible pour les appelants d'oublier ce cas, car ils doivent déballer l'objet eux-mêmes.

Pour ces raisons, nous vous recommandons d'utiliser Optionalcomme type de retour pour vos méthodes, mais pas nécessairement dans vos arguments de méthode.

(Ceci est totalement cribbed, au fait, de la discussion ici .)

Louis Wasserman
la source
3
+1 pour une excellente explication. Je ne sais pas si c'est l'inspiration, mais j'ajouterais un pointeur sur les types d'options dans SML, OCaml et F #, qui ont beaucoup de la même sémantique.
Adam Mihalcin
2
C'est toujours formidable d'obtenir une réponse de quelqu'un qui était directement impliqué. J'aurais attribué +1 juste pour ça. Souhaitez +1 de plus pour une excellente réponse. (mais je ne peux pas.) Je pense que le fait de l'avoir comme valeur de retour pour forcer les consommateurs d'une méthode inconnue à faire un effort pour reconnaître qu'un «rien» peut être retourné est une raison convaincante. Je n'aime pas la façon dont il est surutilisé (ou même abusé). Par exemple, je me sens vraiment mal à l'aise quand je vois des gens mettre des options comme valeurs dans Maps. Map returing null pour une clé inexistante / non mappée est un paradigme bien établi ... Pas besoin de compliquer les choses ...
RAY
9
Il est bien établi qu'il Mapretourne nullsi une clé n'est pas mappée, mais rappelez-vous que si vous le faites map.put(key, null), alors map.containsKey(key)reviendra truemais map.get(key)reviendra null. Optionalpeut être utile pour éclaircir la distinction entre le cas "explicitement mappé à nul" et le cas "absent de la carte". J'accorde que cela Optionalpeut être abusé, mais je ne suis pas encore convaincu que le cas que vous décrivez est un abus.
Louis Wasserman
8
Pour utiliser une analogie désuète, il y avait ces choses géantes appelées «annuaires téléphoniques» :-) et si je vous demandais de rechercher le numéro de quelqu'un et que vous disiez «il n'y a pas de numéro pour cette personne» je dirais «que faites-vous ça veut dire, ils sont là-dedans avec un numéro non répertorié ou vous n'avez tout simplement pas pu trouver une entrée pour eux? " Ces deux réponses possibles correspondent parfaitement à Optional.absent () et null respectivement. Optional.absent () est le "négatif positif" définitif.
Kevin Bourrillion
3
@RAY: D'une certaine manière, Optional<T> est-ce que cette valeur est "trouvée, mais non valide". Ou plus précisément, Optional<T>c'est un moyen de décorer n'importe quel type Tavec une valeur supplémentaire "trouvé, mais non valide" - en créant un nouveau type en combinant deux types existants. Si vous avez une centaine de classes, il peut être compliqué de devoir créer une valeur distincte "trouvée, mais non valide" pour chacune, mais Optional<T>peut facilement fonctionner pour toutes.
Daniel Pryden
9

Cela ressemble vraiment au Maybemodèle Monad de Haskell.

Vous devriez lire ce qui suit, Wikipedia Monad (programmation fonctionnelle) :

Et lisez From Optional to Monad with Guava sur le blog de Kerflyn, qui traite de l'option facultative de Guava utilisée comme monade:


Edit: Avec Java8, il existe une option intégrée qui a des opérateurs monadiques comme flatMap. Cela a été un sujet controversé mais a finalement été mis en œuvre.

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

L' flatMapopérateur est essentiel pour permettre des opérations monadiques, et permet d'enchaîner facilement les appels qui renvoient tous des résultats optionnels.

Pensez-y, si vous utilisiez l' mapopérateur 5 fois, vous vous retrouveriez avec un Optional<Optional<Optional<Optional<Optional<String>>>>>, tandis que l'utilisation flatMapvous donneraitOptional<String>

Depuis Java8, je préférerais ne pas utiliser le Guava's Optional qui est moins puissant.

Sébastien Lorber
la source
6

Une bonne raison de l'utiliser est qu'il rend vos nulls très significatifs. Au lieu de renvoyer un null qui pourrait signifier beaucoup de choses (comme une erreur, un échec, ou vide, etc.), vous pouvez mettre un «nom» à votre null. Regardez cet exemple:

définissons un POJO de base:

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

}

Utilisons maintenant ce simple 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);   
    }
}

Évitons maintenant d'utiliser null et faisons nos vérifications avec Optionnel pour que ce soit significatif

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

ainsi à la fin c'est un moyen de rendre les nulls significatifs, et moins d'ambiguïté.

j2emanue
la source
4

L'avantage le plus important de Optional est qu'il ajoute plus de détails au contrat entre l'implémenteur et l'appelant d'une fonction. Pour cette raison, il est à la fois utile pour les paramètres et le type de retour.

Si vous faites en sorte que la convention ait toujours Optionalpour les objets nuls possibles, vous ajoutez plus de clarifications à des cas tels que:

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

    Le contrat ici spécifie clairement qu'il y a une chance qu'un résultat ne soit pas renvoyé, mais montre également qu'il fonctionnera avec fromet tocomme absent.

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

    Le contrat spécifie que le from est facultatif, donc une valeur absente peut avoir une signification spéciale comme commencer à partir de 2. Je peux m'attendre à ce qu'une valeur nulle pour le toparamètre lèvera une exception.

Donc, la bonne partie de l'utilisation d'Optionnel est que le contrat est devenu à la fois descriptif (similaire avec @NotNullannotation) mais également formel puisque vous devez écrire du code .get()pour y faire face Optional.

raisins secs
la source
Pourquoi utiliser une <List> facultative alors qu'une liste vide serait plus propre?
RAY
J'ai choisi le mauvais exemple au retour. Avec les collections, vous pouvez faire en sorte que la convention renvoie toujours une collection vide à la place d'une valeur nulle. Si cette convention est utilisée, vous n'avez pas besoin de l'option facultative pour les collections. Facultatif peut être perçu comme une collection avec zéro ou un élément, il n'est donc pas nécessaire de le placer autour d'une autre collection.
raisercostin le
2
selon le contexte, il pourrait y avoir une différence significative entre une liste avec 0 élément et une liste absente.
plasma147