web-dev-qa-db-fra.com

Dois-je utiliser Java8 / Guava facultatif pour chaque méthode qui peut retourner null?

Facultatif est utilisé pour représenter un objet nullable, certaines utilisations de cette classe incluent

  1. Comme type de retour de méthode, comme alternative au retour de null à
    indique qu'aucune valeur n'était disponible
  2. Distinguer "inconnu" (par exemple, non présent sur une carte) et "connu pour n'avoir aucune valeur" (présent sur la carte, avec valeur
    Facultatif.absent ())
  3. Pour encapsuler des références nullables pour le stockage dans une collection qui ne prend pas en charge null (bien qu'il existe plusieurs autres approches à cela qui doivent être considérées en premier)

Pour le premier cas, dois-je retourner facultatif dans toutes les méthodes de retour nullable?

60
lessisawesome

Alors, quel est le problème avec l'option?

La question à laquelle nous sommes confrontés est la suivante: les objets optionnels JDK 8 vont-ils supprimer les références nulles? Et la réponse est un non catégorique! Ainsi, les détracteurs remettent immédiatement en question sa valeur en demandant: alors à quoi cela sert-il que nous ne puissions pas déjà le faire par d'autres moyens?

Contrairement aux langages fonctionnels comme SML ou Haskell qui n'ont jamais eu le concept de références nulles, en Java nous ne pouvons pas simplement nous débarrasser des références nulles qui ont existé historiquement. Cela continuera d'exister, et elles peuvent sans doute ont leurs utilisations appropriées (pour ne citer qu'un exemple: logique à trois valeurs ).

Je doute que l'intention de la classe Optional soit de remplacer chaque référence nullable, mais d'aider à la création d'API plus robustes dans lesquelles, simplement en lisant la signature d'une méthode, nous pourrions dire si nous pouvons nous attendre à une valeur facultative ou non et forcer le programmeur à utiliser cette valeur en conséquence. Mais en fin de compte, Facultatif sera juste une autre référence et soumis aux mêmes faiblesses que toutes les autres références dans la langue (par exemple, vous pourriez retourner un Facultatif nul). Il est bien évident qu'Optional ne va pas sauver la situation.

Comment ces objets optionnels sont censés être utilisés ou s'ils sont valables ou non dans Java a été la question d'un débat animé dans la liste de diffusion lambda du projet. les détracteurs, nous entendons des arguments intéressants comme:

  • Le fait qu'il existe d'autres alternatives (par exemple IDES comme IntelliJ et Eclipse IDE supporte un ensemble de annotations propriétaires pour l'analyse statique de la nullité, le JSR-305 avec des annotations comme @Nullable et @NonNull).
  • Certains voudraient qu'il soit utilisable comme dans le monde fonctionnel , ce qui n'est pas entièrement possible dans Java car le langage manque de nombreuses fonctionnalités existantes dans les langages de programmation fonctionnels comme SML ou Haskell (par exemple, correspondance de motifs).
  • D'autres discutent de la façon dont il est impossible de moderniser le code préexistant pour utiliser cet idiome (par exemple, List.get (Object) qui continuera à retourner null).
  • Et certains se plaignent du fait que le manque de prise en charge du langage pour les valeurs facultatives crée un scénario potentiel dans lequel Facultatif pourrait être tilisé de manière incohérente dans les API, en créant ainsi des incompatibilités, à peu près comme celles que nous aurons avec le reste de l'API Java API qui ne peut pas être mise à niveau pour utiliser la nouvelle classe facultative. C'est à peu près votre question ici. Dans les langues prenant en charge les types facultatifs comme à Ceylan ou comme dans Kotlin , vous ne vous poseriez même pas la question.
  • Un argument convaincant est que si le programmeur invoque la méthode get dans un objet facultatif, s'il est vide, il lèvera une NoSuchElementException, ce qui est à peu près le même problème que nous avons avec les valeurs NULL, juste avec une exception différente.

Ainsi, il semblerait que les avantages d'Optional soient vraiment discutables et probablement limités à l'amélioration de la lisibilité et à l'application des contrats d'interface publique.

Je crois que l'adoption de cet idiome fonctionnel facultatif est susceptible de rendre notre code plus sûr, moins prompt à résoudre les problèmes de déréférencement nul et, par conséquent, plus robuste et moins sujet aux erreurs. Bien sûr, ce n'est pas une solution parfaite car, après tout, les références facultatives peuvent également être définies par erreur sur des références nulles, mais je m'attends à ce que les programmeurs respectent la convention de ne pas passer de références nulles lorsqu'un objet facultatif est attendu, à peu près comme nous considérons aujourd'hui une bonne pratique de ne pas passer une référence nulle là où une collection ou un tableau est attendu, dans ces cas, le bon est de passer un tableau ou une collection vide. Le point ici est que nous avons maintenant un mécanisme dans l'API que nous pouvons utiliser pour rendre explicite que pour une référence donnée, nous ne pouvons pas avoir de valeur à lui attribuer et l'utilisateur est obligé, par l'API, de le vérifier.

Citant article de Google Guava sur l'utilisation d'objets optionnels:

"Outre l'augmentation de la lisibilité qui résulte du fait de donner un nom à null, le plus grand avantage d'Optional est sa résistance à l'idiot. Cela vous oblige à penser activement au cas absent si vous voulez que votre programme soit compilé, car vous devez déballer activement l'option et traiter ce cas ".

Donc, je suppose que c'est à chaque concepteur d'API de choisir jusqu'où ils veulent aller dans l'utilisation d'Optional.

Certains développeurs influents comme Stephen Colebourne et Brian Goetz ont récemment publié quelques articles intéressants sur la bonne utilisation de l'option. J'ai trouvé particulièrement utiles les éléments suivants:

75
Edwin Dalorzo

À titre d'observation, je pense que l'un des aspects les plus importants à prendre en compte lors de la conception d'une application est de décider comment traiter le "problème nul". À cet égard, la première étape consisterait à identifier d'éventuelles "sources" de null. Par exemple, la base de données ou les bibliothèques externes utilisées dans le projet. L'étape suivante consisterait à "contenir" le problème, à savoir envelopper le code problématique (en utilisant Facultatif) et ainsi bloquer la propagation de null dans tout le système, où un code sans méfiance pourrait déclencher des NPE.
Pour répondre à votre question, cela dépend ... La plupart du temps, je dirais que c'est inutile, car cela crée beaucoup de travail sans valeur (par exemple, je n'utiliserais pas facultatif pour les méthodes qui appellent d'autres des méthodes privées à l'intérieur des classes, ou pour des méthodes qui appellent des méthodes de classes package-private), mais du code qui existe à la fine limite de différentes `` préoccupations '' (ou couches) dans votre application, (la signature de les interfaces/classes que vous utilisez pour interroger le magasin de données, par exemple, ou les pojos utilisés pour "transporter" des données qui pourraient avoir des propriétés nulles - DTO, ou, une description plus générale, publié API de différents modules ) devrait éviter de "faire fuir" les valeurs nulles dans certains autres domaines avec des préoccupations différentes.

8
Răzvan Petruescu

Comparé à Guava, un problème ennuyeux avec Java.util.Optional Est qu'il ne fournit pas une méthode comme

orElse(Optional<T>): Optional<T>

qui est, d'autre part, défini dans com.google.common.base.Optional comme

or(Optional<T>): Optional<T>

L'absence de cette fonctionnalité spécifique limite les applications monadiques de Java 8 est facultatif.

MISE À JOUR:

La or(Optional<T>) de la goyave peut être répliquée comme avec Java 8 Facultatif comme

optionalA.map(Optional::of).orElse(optionalB)

ou

optionalA.map(Optional::of).orElseGet(() -> computeOptionalB)

MISE À JOUR:

Dans Java 9 (enfin!), Vous pourrez utiliser Optional.or(Supplier<Optional<T>>):

optionalA.or(() -> computeOptionalB)

C'est bien!

7
Vlad