Prenez les deux exemples de code:
if(optional.isPresent()) {
//do your thing
}
if(variable != null) {
//do your thing
}
Autant que je sache, la différence la plus évidente est que l'option Facultatif nécessite la création d'un objet supplémentaire.
Cependant, de nombreuses personnes ont commencé à adopter rapidement les options. Quel est l'avantage d'utiliser des options par rapport à une vérification nulle?
Optional
exploite le système de typage pour effectuer un travail que vous auriez autrement dû faire tout dans votre tête: se rappeler si une référence donnée peut être null
. C'est bon. Il est toujours intelligent de laisser le compilateur gérer des travaux ennuyeux et de réserver la pensée humaine à un travail créatif et intéressant.
Sans Optional
, chaque référence dans votre code est comme une bombe non explosée. L'accès peut faire quelque chose d'utile, ou bien il peut mettre fin à votre programme avec une exception.
Avec Facultatif et sans null
, chaque accès à une référence normale réussit et chaque référence à un Facultatif réussit à moins qu'il ne soit pas défini et que vous n'ayez pas vérifié cela. C'est une énorme victoire en maintenabilité.
Malheureusement, la plupart des langues qui proposent désormais Optional
n'ont pas été supprimées null
, vous ne pouvez donc que profiter du concept en instituant une politique stricte de "absolument pas de null
, jamais" . Par conséquent, Optional
dans par exemple Java n'est pas aussi convaincant qu'il devrait l'être idéalement.
Un Optional
apporte un typage plus fort dans les opérations qui peuvent échouer, comme les autres réponses l'ont couvert, mais c'est loin de la chose la plus intéressante ou la plus utile Optionals
apporte à la table. Beaucoup plus utile est la possibilité de retarder ou d'éviter la vérification d'échec, et de composer facilement de nombreuses opérations qui peuvent échouer.
Considérez que si vous aviez votre variable optional
à partir de votre exemple de code, vous avez dû effectuer deux étapes supplémentaires qui pourraient potentiellement échouer. Si une étape échoue, vous souhaitez renvoyer une valeur par défaut à la place. En utilisant Optionals
correctement, vous vous retrouvez avec quelque chose comme ceci:
return optional.flatMap(x -> x.anotherOptionalStep())
.flatMap(x -> x.yetAnotherOptionalStep())
.orElse(defaultValue);
Avec null
j'aurais dû vérifier trois fois null
avant de continuer, ce qui ajoute beaucoup de complexité et de maux de tête de maintenance au code. Optionals
a cette vérification intégrée aux fonctions flatMap
et orElse
.
Remarque Je n'ai pas appelé isPresent
une seule fois, ce que vous devriez considérer comme une odeur de code lorsque vous utilisez Optionals
. Cela ne signifie pas nécessairement que vous devriez jamais utiliser isPresent
, juste que vous devriez examiner attentivement tout code qui le fait, pour voir s'il existe une meilleure façon. Sinon, vous avez raison, vous n'obtenez qu'un avantage de sécurité de type marginal sur l'utilisation de null
.
Notez également que je ne suis pas aussi inquiet d'encapsuler tout cela dans une seule fonction, afin de protéger les autres parties de mon code contre les pointeurs nuls des résultats intermédiaires. S'il est plus logique d'avoir ma .orElse(defaultValue)
dans une autre fonction par exemple, j'ai beaucoup moins de scrupules à la mettre là-bas, et il est beaucoup plus facile de composer les opérations entre différentes fonctions selon les besoins.
Il met en évidence la possibilité de null comme réponse valide, que les gens supposent souvent (à tort ou à raison) ne seront pas retournés. Mettre en évidence les quelques fois où null est valide permet d'omettre de jeter votre code avec un grand nombre de vérifications nulles inutiles.
Idéalement, la définition d'une variable sur null serait une erreur de compilation n'importe où, mais en option; éliminer les exceptions de pointeur nul à l'exécution. De toute évidence, la rétrocompatibilité empêche cela, malheureusement
Laisse moi te donner un exemple:
class UserRepository {
User findById(long id);
}
Il est assez clair à quoi cette méthode est destinée. Mais que se passe-t-il si le référentiel ne contient pas d'utilisateur avec un identifiant donné? Il pourrait lever une exception ou peut-être qu'il renvoie null?
Deuxième exemple:
class UserRepository {
Optional<User> findById(long id);
}
Maintenant, que se passe-t-il ici s'il n'y a aucun utilisateur avec un identifiant donné? À droite, vous obtenez une instance Optional.absent () et vous pouvez la vérifier avec Optional.isPresent (). La signature de méthode avec Facultatif est plus explicite sur son contrat. Il est immédiatement clair, comment la méthode est censée se comporter.
Il présente également un avantage lors de la lecture du code client:
User user = userRepository.findById(userId).get();
J'ai formé mon œil pour voir .get () comme une odeur de code immédiate. Cela signifie que le client ignore délibérément le contrat de la méthode invoquée. Il est beaucoup plus facile de voir cela que sans optionnel, car dans ce cas, vous ne savez pas immédiatement quel est le contrat de la méthode appelée (si elle autorise null ou non dans son retour), vous devez donc le vérifier d'abord.
Facultatif est utilisé avec la convention selon laquelle les méthodes avec le type de retour Facultatif ne retournent jamais null et généralement aussi avec la convention (moins forte) cette méthode sans le type de retour facultatif ne renvoie jamais null (mais il est préférable de l'annoter avec @Nonnull, @NotNull ou similaire) .
En plus des autres réponses, l'autre avantage majeur des options quand il s'agit d'écrire du code propre est que vous pouvez utiliser une expression Lambda dans le cas où la valeur est présente, comme indiqué ci-dessous:
opTr.ifPresent(tr -> transactions.put(tr.getTxid(), tr));
Un Optional<T>
Vous permet d'avoir "échec" ou "aucun résultat" comme valeur de réponse/retour valide pour vos méthodes (pensez, par exemple, à une recherche dans la base de données). L'utilisation d'un Optional
au lieu d'utiliser null
pour indiquer un échec/aucun résultat présente certains avantages:
null
peut être retourné.null
devrait, au moins à mon avis, pas être utilisée pour indiquer quoi que ce soit car la sémantique pourrait ne pas être totalement claire. Il doit être utilisé pour indiquer au garbage collector que l'objet mentionné précédemment peut être collecté.Optional
fournit de nombreuses méthodes Nice pour travailler conditionnellement avec les valeurs (par exemple, ifPresent()
) ou définir les valeurs par défaut de manière transparente (orElse()
).Malheureusement, cela ne supprime pas complètement la nécessité de null
vérifications dans le code car l'objet Optional
lui-même peut toujours être null
.
Considérez la méthode suivante:
void dance(Person partner)
Voyons maintenant un code d'appel:
dance(null);
Cela provoque un crash potentiellement difficile à suivre ailleurs, car dance
ne s'attendait pas à une valeur nulle.
dance(optionalPerson)
Cela provoque une erreur de compilation, car la danse attendait un Person
pas un Optional<Person>
dance(optionalPerson.get())
Si je n'avais personne, cela provoque une exception sur cette ligne, au point où j'ai tenté de convertir le Optional<Person>
en une personne. La clé est que, contrairement au passage d'une valeur nulle, les traces sauf ici, pas un autre emplacement dans le programme.
Pour mettre tout cela ensemble: une mauvaise utilisation en option produit plus facilement des problèmes de recherche, une mauvaise utilisation de null entraîne des problèmes difficiles à localiser.