Est-ce que Java 8 getters devrait retourner le type optionnel?

Optional type introduit en Java 8 est une nouvelle chose pour de nombreux développeurs.

est-ce qu'une méthode getter de retour Optional<Foo> type à la place de la classique Foo une bonne pratique? Supposons que la valeur puisse être null .

211
demandé sur Nayuki 2014-10-12 21:30:18

5 réponses

bien sûr, les gens feront ce qu'ils veulent. Mais nous avons eu une intention claire en ajoutant cette caractéristique, et il était pas d'être un but général peut-être type, autant que beaucoup de gens auraient aimé que nous le fassions. Notre intention était de fournir un mécanisme limité pour les types de retour de méthode de bibliothèque où il devait y avoir un moyen clair de représenter "aucun résultat", et l'utilisation de null pour ce type était très susceptible de causer des erreurs.

par exemple, vous ne devriez probablement jamais l'utiliser pour quelque chose qui retourne un tableau de résultats, ou une liste de résultats; au lieu de retourner un tableau vide ou une liste. Vous devriez presque jamais l'utiliser comme un champ de quelque chose ou d'un paramètre de méthode.

je pense que l'utiliser régulièrement comme valeur de retour pour getters serait certainement Sur-utilisé.

il n'y a rien mauvais avec option qu'il devrait être évité, il est tout simplement pas ce que beaucoup de gens souhaitent qu'il soit, et en conséquence nous étions assez préoccupés par le risque de surutilisation zélée.

(annonce de service Public: JAMAIS appel Optional.get sauf si vous pouvez prouver qu'il ne sera jamais nulle; à la place, utilisez l'une des méthodes sûres comme orElse ou ifPresent . Rétrospectivement, nous aurions dû appeler get quelque chose comme getOrElseThrowNoSuchElementException ou quelque chose qui a rendu beaucoup plus clair que c'était une méthode très dangereuse qui a ébranlé tout le but de Optional en premier lieu. Leçon apprise. (Mise à jour: Java 10 a Optional.orElseThrow() , ce qui est sémantiquement équivalent à get() , mais dont le nom est plus approprié.))

358
répondu Brian Goetz 2018-09-12 12:23:55

après avoir fait un peu de recherche de ma part, j'ai rencontré un certain nombre de choses qui pourraient suggérer quand cela est approprié. La plus importante étant la citation suivante tirée d'un article D'Oracle:

" il est important de noter que l'intention de la classe facultative est de ne pas remplacer chaque référence nulle . Au lieu de cela, son but est d'aider à concevoir APIs plus compréhensible ainsi qu'en lisant simplement la signature d'une méthode, vous pouvez dire si vous pouvez vous attendre à une valeur optionnelle. Cela vous oblige à déballer activement un optionnel pour faire face à l'absence d'une valeur." - fatigué des Exceptions au pointeur nul? Envisagez D'utiliser Java SE 8 En Option!

j'ai aussi trouvé cet extrait de Java 8 optionnel: comment l'utiliser

"Optional n'est pas destiné à être utilisé dans ces contextes, car il ne nous achètera rien:

  • dans la couche domain model (Non sérialisable)
  • dans les Dto (même raison)
  • dans les paramètres d'entrée des méthodes
  • dans les paramètres du constructeur "

Qui semble également soulever quelques points valables.

Je n'ai pas pu trouver de connotations négatives ou de signaux d'alarme suggérant que Optional devrait être évité. Je pense que l'idée générale est, si elle est utile ou améliore la convivialité de votre API, utilisez-la.

59
répondu Justin 2018-09-04 20:20:28

je dirais qu'en général c'est une bonne idée d'utiliser le type optionnel pour les valeurs de retour qui peuvent être nulles. Cependant, W. R. T. pour les frameworks, je suppose que le remplacement des getters classiques par des types optionnels causera beaucoup de problèmes lorsque l'on travaille avec des frameworks (par exemple Hibernate) qui s'appuient sur des conventions de codage pour les getters et les setters.

12
répondu Claas Wilke 2014-10-12 17:43:36

la raison pour laquelle Optional a été ajouté à Java est parce que:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

est plus propre que ceci:

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;

mon point est que optionnel a été écrit pour supporter la programmation fonctionnelle , qui a été ajouté à Java en même temps. (L'exemple est fourni par le blog de Brian Goetz . Un meilleur exemple pourrait utiliser la méthode orElse() , puisque ce code va jeter une exception de toute façon, mais vous obtenez l'image.)

mais maintenant, les gens utilisent optionnel pour une raison très différente. Ils s'en servent pour corriger une faille dans la conception du langage. Le défaut est le suivant: il n'y a aucun moyen de spécifier quels paramètres D'une API et quelles valeurs de retour sont autorisés à être nuls. Il peut être mentionné dans les javadocs, mais la plupart des développeurs n'écrivent même pas javadocs pour leur code, et peu vérifieront les javadocs pendant qu'ils écrivent. Ceci conduit donc à beaucoup de code qui toujours vérifie les valeurs nulles avant de les utiliser, même si elles ne peuvent souvent pas être nulles parce qu'elles ont déjà été validées à plusieurs reprises neuf ou dix fois plus haut que la pile d'appels.

je pense qu'il y avait une réelle soif de résoudre ce défaut, parce que tant de gens qui ont vu la nouvelle classe optionnelle ont supposé que son but était d'ajouter de la clarté aux API. C'est pourquoi les gens posent des questions comme "devrait getters retour Optionals?"Non, ils ne devraient probablement pas, à moins que vous vous attendiez à ce que le getter soit utilisé dans programmation fonctionnelle, ce qui est très peu probable. En fait, si vous regardez où Optional est utilisé dans L'API Java, c'est principalement dans les classes Stream, qui sont au cœur de la programmation fonctionnelle. (Je n'ai pas vérifié très attentivement, mais les classes de flux pourraient être le seulement endroit ils sont utilisés.)

Si vous ne prévoyez d'utiliser un getter dans un peu de code fonctionnel, il pourrait être une bonne idée d'avoir un getter et un deuxième qui renvoie en Option.

Oh, et si vous avez besoin de votre classe pour être sérialisable, vous ne devez absolument pas utiliser optionnel.

les options sont une très mauvaise solution au problème de L'API car a) elles sont très verbeuses, et b) elles n'ont jamais été conçues pour résoudre ce problème en premier lieu.

une bien meilleure solution au défaut de L'API est le Nullness Checker . C'est un processeur d'annotation qui permet de spécifier les paramètres et les valeurs de retour peuvent être nulles en les annotant avec @Nullable. De cette façon, le compilateur peut balayer le code et comprendre si une valeur qui peut effectivement être nulle est passée à une valeur où nul n'est pas autorisé. Par défaut, il n'assume rien n'est autorisé à être nul, sauf s'il est annoté. De cette façon, vous n'avez pas à vous soucier des valeurs nulles. Passant une valeur null à un paramètre entraînera une erreur de compilation. Tester un objet pour null qui ne peut pas être null produit un avertissement de compilateur. Le cela a pour effet de modifier NullPointerException d'une erreur d'exécution à une erreur de compilation.

cela change tout.

quant à vos getters, n'utilisez pas optionnel. Et essayez de concevoir vos classes de sorte qu'aucun des membres ne puisse être null. Et peut-être essayer d'ajouter le vérificateur de Nullness à votre projet et déclarer vos getters et paramètres de setter @Nullable s'ils en ont besoin. Je n'ai fait ça qu'avec de nouveaux projets. Il produit probablement beaucoup de avertissements dans les projets existants écrits avec beaucoup de tests superflus pour null, il pourrait donc être difficile à rénover. Mais il va aussi attraper beaucoup de bugs. Je l'aime. Mon code est beaucoup plus propre et plus fiable à cause de cela.

(il y a aussi un nouveau langage qui aborde cette question. Kotlin, qui compile vers le code byte Java, vous permet de spécifier si un objet peut être nul lorsque vous le déclarez. C'est un nettoyant approche.)

Addendum à L'Original Post (version 2)

après y avoir beaucoup réfléchi, j'en suis arrivé à la conclusion qu'il est acceptable de retourner optionnel à une condition: que la valeur récupérée puisse effectivement être nulle. J'ai vu beaucoup de code où les gens reviennent en option de getters qui ne peuvent pas retourner null. Je vois cela comme une très mauvaise pratique de codage qui ne fait qu'ajouter de la complexité au code, ce qui rend les bogues plus probables. Mais quand la valeur retournée pourrait en fait être null, allez-y et envelopper dans un optionnel.

garder à l'esprit que les méthodes qui sont conçues pour la programmation fonctionnelle, et qui nécessitent une référence de fonction, seront (et devraient) être écrites sous deux formes, dont l'une est facultative. Par exemple, Optional.map() et Optional.flatMap() prennent tous deux des références de fonction. Le premier prend une référence à un getter ordinaire, et le second prend un qui retourne optionnel. Donc tu ne rends à personne une faveur en rendant un Optionnel lorsque la valeur ne peut pas être nulle.

cela dit, je vois encore que l'approche utilisée par le Nullness Checker est la meilleure façon de gérer les null, puisqu'ils tournent NullPointerExceptions des bogues d'exécution pour compiler les erreurs de temps.

4
répondu MiguelMunoz 2018-10-06 03:29:46

Si vous êtes en utilisant les sérialiseurs et d'autres cadres que de comprendre Optional puis j'ai trouvé ces lignes directrices bien travailler lors de l'écriture de Entity les haricots et le domaine des couches:

  1. si la couche de sérialisation (habituellement un DB) permet une valeur null pour une cellule dans la colonne BAR dans le tableau FOO , alors le getter Foo.getBar() peut retourner Optional indiquant le développeur que cette valeur peut raisonnablement s'attendre à être null et qu'ils doivent les gérer. Si le DB garantit que la valeur ne sera pas nulle, alors le getter devrait et non l'envelopper dans un Optional .
  2. Foo.bar doit être private et pas être Optional . Il n'y a vraiment aucune raison pour que ce soit Optional si c'est private .
  3. le setter Foo.setBar(String bar) doit prendre le type de bar et et non Optional . S'il est correct d'utiliser un argument null , il faut l'indiquer dans le commentaire JavaDoc. S'il n'est pas correct d'utiliser null an IllegalArgumentException ou une logique commerciale appropriée est, IMHO, plus approprié.
  4. les constructeurs n'ont pas besoin d'arguments Optional (pour des raisons similaires au point 3). En général, je n'inclus que des arguments dans le constructeur que doit doit être non null dans le la sérialisation de la base de données.

pour rendre le ci-dessus plus efficace, vous pourriez vouloir éditer vos gabarits D'IDE pour générer des getters et les gabarits correspondants pour toString() , equals(Obj o) etc. ou utilisez les champs directement pour ceux-ci (la plupart des générateurs D'IDE traitent déjà des nulls).

2
répondu Mark 2017-07-30 19:20:32