J'ai récemment lu des articles à ce sujet et vu des personnes utiliser cette classe, mais dans la plupart des cas, l'utilisation de la classe null
aurait aussi bien fonctionné, si ce n'est plus intuitivement. Quelqu'un peut-il fournir un exemple concret où Optional
réaliserait quelque chose qui null
ne pourrait pas ou d'une manière beaucoup plus propre ? La seule chose à laquelle je pense est de l'utiliser avec Maps
qui n'acceptent pas null
mais même cela pourrait être fait avec un "mappage" latéral de la valeur de null. Quelqu'un peut-il me fournir un argument plus convaincant ? Merci.
Réponses
Trop de publicités?Membre de l'équipe Guava ici.
L'inconvénient le plus important de null
est qu'il n'est pas évident de savoir ce qu'il doit signifier dans un contexte donné : il n'a pas de nom illustratif. Il n'est pas toujours évident que null
signifie "pas de valeur pour ce paramètre" -- ou encore, en tant que valeur de retour, cela signifie parfois "erreur", ou même "succès" ( !!), ou simplement "la réponse correcte est rien". Optional
est souvent le concept que vous entendez réellement lorsque vous rendez une variable nullable, mais pas toujours. Lorsque ce n'est pas le cas, nous vous recommandons d'écrire votre propre classe, similaire à la classe Optional
mais avec un système de dénomination différent, afin de clarifier ce que vous voulez vraiment dire.
Mais je dirais que le plus grand avantage de Optional
n'est pas dans la lisibilité : l'avantage est qu'il est à l'abri des erreurs. Il vous oblige à penser activement au cas absent si vous voulez que votre programme compile, puisque vous devez déballer activement le code de l'option Optional
et traiter ce cas. Avec Null, il est extrêmement facile d'oublier des choses, et bien que FindBugs soit utile, je ne pense pas qu'il aborde le problème aussi bien. Ceci est particulièrement pertinent lorsque vous retournez des valeurs qui peuvent ou non être "présentes". Vous (et d'autres) êtes beaucoup plus susceptibles d'oublier que other.method(a, b)
pourrait retourner un null
que vous risquez d'oublier que a
pourrait être null
lorsque vous mettez en œuvre other.method
. Retourner à Optional
rend impossible pour les appelants d'oublier ce cas, puisqu'ils doivent déballer l'objet eux-mêmes.
Pour ces raisons, nous vous recommandons d'utiliser Optional
comme type de retour pour vos méthodes, mais pas nécessairement dans vos arguments de méthode.
(Ceci est d'ailleurs totalement tiré de la discussion ici .)
On dirait vraiment que le Maybe
Modèle de monade de Haskell.
Vous devriez lire ce qui suit, Wikipedia Monade (programmation fonctionnelle) :
Et lire De l'option à la monade avec Guava sur Le blog de Kerflyn, qui traite de l'option de la goyave utilisée comme monade :
Edit : Avec Java8, il y a un Facultatif intégré qui a des opérateurs monadiques comme flatMap
. Ce sujet a été controversé, mais il 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);
Le site flatMap
est essentiel pour permettre les opérations monadiques, et permet d'enchaîner facilement des appels qui renvoient tous des résultats optionnels.
Pensez-y, si vous avez utilisé le map
opérateur 5 fois, vous vous retrouveriez avec un Optional<Optional<Optional<Optional<Optional<String>>>>>
tout en utilisant flatMap
vous donnerait Optional<String>
Depuis Java8, je préfère ne pas utiliser l'option de Guava qui est moins puissante.
L'une des bonnes raisons de l'utiliser est qu'elle rend les nullités très significatives. Au lieu de renvoyer un null qui pourrait signifier beaucoup de choses (comme une erreur, un échec, un vide, etc.), vous pouvez donner 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 cette 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);
}
}
Maintenant, évitons d'utiliser null et faisons nos vérifications avec Optional 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, en fin de compte, il s'agit d'un moyen de rendre les valeurs nulles significatives et de réduire l'ambiguïté.
L'avantage le plus important de l'option est qu'elle ajoute plus de détails au contrat entre l'implémenteur et l'appelant d'une fonction. Pour cette raison, il est utile à la fois pour les paramètres et le type de retour.
Si vous faites la convention de toujours avoir Optional
pour d'éventuels objets nuls, vous apportez des clarifications supplémentaires à des cas comme celui-ci :
-
Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)
Le contrat spécifie clairement qu'il y a une chance qu'un résultat ne soit pas retourné, mais montre également qu'il fonctionnera avec
from
etto
comme absent. -
Optional<Integer> maxPrime(Optional<Integer> from, Integer to)
Le contrat précise que l'adhésion est facultative. absent peut avoir une signification particulière, comme commencer à partir de 2. Je peux m'attendre à ce qu'une valeur nulle pour la valeur
to
entraînera une exception.
L'avantage de l'utilisation de l'option est que le contrat devient à la fois descriptif (similaire à l'option @NotNull
) mais aussi formelle puisque vous devez écrire du code .get()
pour faire face à Optional
.