Le Java 8 getters devrait-il retourner le type facultatif?

Optional type Optional introduit dans Java 8 est une nouveauté pour de nombreux développeurs.

Est-ce qu’une méthode getter retourne Optional taper à la place du classique Foo une bonne pratique? Supposons que la valeur puisse être null .

Bien sûr, les gens vont faire ce qu’ils veulent. Mais nous avions une intention claire en ajoutant cette fonctionnalité, et ce n’était pas un but général. Peut-être scala.Option type scala.Option , 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 utiliser null pour un tel risque de provoquer des erreurs.

Par exemple, vous ne devriez probablement jamais l’utiliser pour quelque chose qui renvoie un tableau de résultats ou une liste de résultats; renvoie plutôt un tableau ou une liste vide. Vous ne devriez presque jamais l’utiliser comme un champ de quelque chose ou un paramètre de méthode.

Je pense que l’utiliser régulièrement comme valeur de retour pour les getters serait certainement une utilisation excessive.

Il n’ya rien de mal à faire en sorte que cela soit évité, ce n’est pas ce que beaucoup de gens souhaitent, et en conséquence, nous étions assez préoccupés par le risque de surutilisation excessive.

(Annonce de service public: N’appelez JAMAIS Optional.get moins que vous ne puissiez prouver qu’il ne sera jamais nul; utilisez plutôt une des méthodes sûres comme orElse ou ifPresent . Rétrospectivement, nous aurions dû appeler getOrElseThrowNoSuchElementException ou quelque chose qui le getOrElseThrowNoSuchElementException plus clair Leçon apprise (UPDATE: Java 10 a Optional.orElseThrow() , qui est sémantiquement équivalent à get() , mais dont le nom est plus approprié. ))

Après avoir fait quelques recherches personnelles, je suis tombé sur un certain nombre de choses qui pourraient suggérer quand cela serait approprié. Le plus important étant la citation suivante d’un article Oracle:

“Il est important de noter que l’intention de la classe Facultatif n’est pas de remplacer toutes les références null . Au lieu de cela, elle a pour but d’aider à concevoir des API plus compréhensibles afin que vous lisiez simplement la signature d’une méthode. peut s’attendre à une valeur facultative, ce qui vous oblige à dérouler activement une option pour faire face à l’absence d’une valeur. “ – Fatigué des exceptions de pointeur nul? Envisagez d’utiliser Java SE 8 en option!

J’ai également trouvé cet extrait de Java 8 Facultatif: comment l’utiliser

“Facultatif n’est pas destiné à être utilisé dans ces contextes, car il ne nous achètera rien:

  • dans la couche de modèle de domaine (non sérialisable)
  • dans les DTO (même raison)
  • dans les parameters d’entrée des méthodes
  • dans les parameters du constructeur “

Ce qui semble également soulever des points valables.

Je n’ai pas pu trouver de connotations négatives ou de drapeaux rouges pour suggérer que Optional devrait être évité. Je pense que l’idée générale est que, si elle est utile ou améliore la convivialité de votre API, utilisez-la.

Je dirais en général que c’est une bonne idée d’utiliser le type facultatif pour les valeurs de retour pouvant être nulles. Cependant, selon les frameworks, je suppose que le remplacement des getters classiques par des types optionnels posera beaucoup de problèmes lors de l’utilisation de frameworks (par exemple, Hibernate) reposant sur des conventions de codage pour les getters et les setters.

La raison pour laquelle Optional été ajouté à Java est parce que ceci:

 return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods()) .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(...)); 

c’est mieux que ça:

 Method matching = Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods()) .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 facultatif a été écrit pour prendre en charge la functional programming , qui a été ajoutée à Java en même temps. (L’exemple est fourni par un blog de Brian Goetz. Un meilleur exemple pourrait utiliser la méthode orElse() , car ce code jettera une exception quand même, mais vous obtenez l’image.)

Mais maintenant, les gens utilisent Optionnel pour une raison très différente. Ils l’utilisent pour corriger une faille dans la conception de la langue. La faille est la suivante: il n’y a aucun moyen de spécifier lequel des parameters d’une API et les valeurs de retour peuvent être nulles. Cela peut être mentionné dans les javadocs, mais la plupart des développeurs n’écrivent même pas de javadocs pour leur code, et peu d’entre eux vérifieront les javadocs à mesure qu’ils écrivent. Cela conduit donc à beaucoup de code qui vérifie toujours les valeurs NULL avant de les utiliser, même si elles ne peuvent souvent pas être NULL car elles ont déjà été validées à plusieurs resockets neuf ou dix fois la stack d’appels.

Je pense qu’il y avait une vraie soif de résoudre ce problème, parce que tant de personnes qui ont vu la nouvelle classe facultative ont supposé que son objective était de clarifier les API. Quelle est la raison pour laquelle les gens posent des questions telles que “les gagnants devraient-ils retourner les options?” Non, ils ne devraient probablement pas, à moins que vous vous attendiez à ce que le getter soit utilisé dans la functional programming, ce qui est très improbable. 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 functional programming. (Je n’ai pas vérifié très attentivement, mais les classes Stream peuvent être le seul endroit où elles sont utilisées.)

Si vous prévoyez d’utiliser un getter dans un peu de code fonctionnel, il peut être judicieux d’avoir un getter standard et un second qui renvoie le paramètre Facultatif.

Oh, et si vous avez besoin que votre classe soit sérialisable, vous ne devriez absolument pas utiliser Facultatif.

Les options sont une très mauvaise solution à la faille de l’API car a) elles sont très détaillées, et b) elles n’ont jamais été conçues pour résoudre ce problème en premier lieu.

Une solution bien meilleure à la faille API est le vérificateur de nullité . Il s’agit d’un processeur d’annotations qui vous permet de spécifier quels parameters et valeurs de retour peuvent être nuls en les annotant avec @Nullable. De cette façon, le compilateur peut parsingr le code et déterminer si une valeur pouvant être réellement nulle est transmise à une valeur où la valeur null n’est pas autorisée. Par défaut, il est supposé que rien n’est autorisé à être nul à moins d’être annoté. De cette façon, vous n’avez pas à vous soucier des valeurs nulles. Passer une valeur nulle à un paramètre entraînera une erreur de compilation. Tester un object pour null qui ne peut pas être nul génère un avertissement du compilateur. Cela a pour effet de remplacer NullPointerException par une erreur d’exécution par une erreur de compilation.

Cela change tout.

Pour ce qui est de vos getters, n’utilisez pas l’option Facultatif. Et essayez de concevoir vos classes afin qu’aucun membre ne puisse être nul. Et peut-être essayer d’append le vérificateur de nullité à votre projet et déclarer vos getters et parameters de setter @Nullable s’ils en ont besoin. Je l’ai seulement fait avec de nouveaux projets. Il produit probablement beaucoup d’avertissements dans les projets existants écrits avec beaucoup de tests superflus pour null, donc il pourrait être difficile de les remplacer. Mais il va aussi attraper beaucoup de bugs. J’aime ça. Mon code est beaucoup plus propre et fiable grâce à cela.

(Il y a aussi un nouveau langage qui traite de ceci. Kotlin, qui comstack en code d’octet Java, vous permet de spécifier si un object peut être nul lorsque vous le déclarez. C’est une approche plus propre.)

Si vous utilisez des sérialiseurs modernes et d’autres frameworks qui comprennent les Optional alors j’ai trouvé que ces instructions fonctionnaient bien lors de l’écriture des couches et des couches de domaine Entity :

  1. Si la couche de sérialisation (généralement une firebase database) autorise une valeur null pour une cellule de la colonne BAR dans la table FOO , le getter Foo.getBar() peut renvoyer une Optional indiquant au développeur que cette valeur peut raisonnablement être nulle et que devrait gérer cela. Si la firebase database garantit que la valeur ne sera pas nulle, le getter ne devrait pas l’ envelopper dans une Optional .
  2. Foo.bar devrait être private et ne pas être Optional . Il n’y a vraiment aucune raison pour que ce soit Optional s’il est private .
  3. Le setter Foo.setBar(Ssortingng bar) doit prendre le type de bar et non pas Optional . Si vous pouvez utiliser un argument null , indiquez-le dans le commentaire JavaDoc. Si ce n’est pas correct d’utiliser null une IllegalArgumentException ou une logique métier appropriée est, à IllegalArgumentException , plus appropriée.
  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 qui doivent être non nuls dans la firebase database de sérialisation.

Pour rendre ce qui précède plus efficace, vous pouvez éditer vos modèles IDE pour générer des getters et des modèles correspondants pour toSsortingng() , equals(Obj o) etc.