Pourquoi l'option facultative de Java 8 ne devrait-elle pas être utilisée dans les arguments

392

J'ai lu sur de nombreux sites Web. Facultatif doit être utilisé uniquement comme type de retour et non utilisé dans les arguments de méthode. J'ai du mal à trouver une raison logique pour laquelle. Par exemple, j'ai un morceau de logique qui a 2 paramètres facultatifs. Par conséquent, je pense qu'il serait logique d'écrire ma signature de méthode comme ceci (solution 1):

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
    // my logic
}

De nombreuses pages Web spécifient que Facultatif ne doit pas être utilisé comme arguments de méthode. Dans cet esprit, je pourrais utiliser la signature de méthode suivante et ajouter un commentaire Javadoc clair pour spécifier que les arguments peuvent être nuls, en espérant que les futurs responsables liront le Javadoc et effectueront donc toujours des vérifications nulles avant d'utiliser les arguments (solution 2) :

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

Alternativement, je pourrais remplacer ma méthode par quatre méthodes publiques pour fournir une interface plus agréable et la rendre plus évidente p1 et p2 sont facultatifs (solution 3):

public int calculateSomething() {
    calculateSomething(null, null);
}

public int calculateSomething(String p1) {
    calculateSomething(p1, null);
}

public int calculateSomething(BigDecimal p2) {
    calculateSomething(null, p2);
}

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

J'essaie maintenant d'écrire le code de la classe qui invoque ce morceau de logique pour chaque approche. Je récupère d'abord les deux paramètres d'entrée d'un autre objet qui retourne Optionals puis, j'invoque calculateSomething. Par conséquent, si la solution 1 est utilisée, le code appelant ressemblerait à ceci:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);

si la solution 2 est utilisée, le code appelant ressemblerait à ceci:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

si la solution 3 est appliquée, je pourrais utiliser le code ci-dessus ou je pourrais utiliser ce qui suit (mais c'est beaucoup plus de code):

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p1, p2);
    } else {
        result = myObject.calculateSomething(p1);
    }
} else {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p2);
    } else {
        result = myObject.calculateSomething();
    }
}

Ma question est donc la suivante: pourquoi est-il considéré comme une mauvaise pratique d'utiliser Optionals comme arguments de méthode (voir la solution 1)? Cela ressemble à la solution la plus lisible pour moi et rend plus évident que les paramètres pourraient être vides / nuls pour les futurs responsables. (Je sais que les concepteurs l'ont Optionalprévu pour être utilisé uniquement comme type de retour, mais je ne trouve aucune raison logique de ne pas l'utiliser dans ce scénario).

Neil Stevens
la source
16
Si vous avez utilisé des optionnels, ne devriez-vous pas vérifier que l'option facultative passée en paramètre ne l'est pas null?
biziclop
17
Oui, mais il serait évident pour quelqu'un d'autre de maintenir le code à l'avenir que le paramètre peut être vide / nul, évitant ainsi potentiellement une exception de pointeur nul à l'avenir
Neil Stevens
1
Sensationnel. Des arguments nuls sont-ils passés à une méthode? C'est tellement Visual Basic. Quel principe viole-t-il? SRP (peut-être). Il viole également un autre principe dont le nom me laisse sans fondement va dans le sens de transmettre SEULEMENT les informations nécessaires pour qu'une méthode ou une fonction fasse son travail.
Luminous
20
Tout ce qui est théoriquement nul peut être comme toutes les méthodes d'une bibliothèque appelant éventuellement System.exit (0). Vous ne pouvez pas vérifier cela et vous ne devriez pas vérifier cela. Tout ce que vous auriez à faire tout le temps que vous ne devriez (presque) jamais faire. Comme rendre tous les paramètres définitifs. Laissez vos outils vous aider à éviter de modifier les valeurs des paramètres ou d'oublier d'initialiser les champs au lieu de rendre votre code illisible par mille finales et autant de vérifications nulles.
yeoman
6
En fait, utilisez simplement les annotations Non Null / Nullable, c'est ce que vous recherchez dans cette situation, pas facultatif.
Bill K

Réponses:

202

Oh, ces styles de codage doivent être pris avec un peu de sel.

  1. (+) Passer un résultat facultatif à une autre méthode, sans aucune analyse sémantique; laisser cela à la méthode, c'est bien.
  2. (-) L'utilisation de paramètres facultatifs provoquant une logique conditionnelle à l'intérieur des méthodes est littéralement contre-productive.
  3. (-) La nécessité de compresser un argument dans un Facultatif, n'est pas optimale pour le compilateur et effectue un habillage inutile.
  4. (-) Par rapport aux paramètres annulables, l'option est plus coûteuse.

En général: facultatif unifie deux états, qui doivent être démêlés. Par conséquent, mieux adapté au résultat qu'à l'entrée, pour la complexité du flux de données.

Joop Eggen
la source
38
En fait, avoir un Optionalparamètre représente l'un des trois états: un nullfacultatif, un non nul Optionalavec isPresent() == falseet un non nul Optionalencapsulant une valeur réelle.
biziclop
16
@biziclop oui, un point incontournable déjà critiqué. Mais l' intention est d'avoir uniquement des expressions non nulles. Qu'il n'ait pas fallu longtemps, entendre les conseils pour éviter facultatif dans certains cas aussi, est assez ironique. A @NotNull Optional.
Joop Eggen
80
@biziclop Notez que si vous utilisez Optionaldu tout, alors l'état 1 ( nullfacultatif) indique généralement une erreur de programmation, vous pouvez donc aussi ne pas la gérer (laissez-la simplement jeter un NullPointerException)
user253751
50
"sous-optimal pour le compilateur", "Par rapport aux paramètres nullables, l'option est plus coûteuse" - ces arguments pourraient être valides pour le langage C et non pour le langage Java. Les programmeurs Java devraient se concentrer sur le code propre, la portabilité, la testabilité, la bonne architecture, la modularité, etc., et non sur "Optionnel est plus coûteux que la référence nulle". Et si vous trouvez que vous devez vous concentrer sur les micro-optimisations, vous feriez mieux de sauter les objets, les listes, les génériques et de passer aux tableaux et aux primitives (je ne veux pas être offensant ici, je partage juste mon opinion) .
Kacper86
15
"sous-optimal pour le compilateur": l'optimisation prématurée est la racine de tout mal ... si vous n'écrivez pas de code critique pour les performances (ce qui est souvent le cas lorsque vous essayez de spécifier une interface propre), cela ne devrait pas être un problème.
Mohan
165

Le meilleur article que j'ai vu sur le sujet a été écrit par Daniel Olszewski :

Bien qu'il puisse être tentant de considérer Facultatif pour les paramètres de méthode non obligatoires, une telle solution est pâle en comparaison avec d'autres alternatives possibles. Pour illustrer le problème, examinez la déclaration de constructeur suivante:

public SystemMessage(String title, String content, Optional<Attachment> attachment) {
    // assigning field values
}

À première vue, cela peut sembler une bonne décision de conception. Après tout, nous avons explicitement marqué le paramètre d'attachement comme facultatif. Cependant, comme pour appeler le constructeur, le code client peut devenir un peu maladroit.

SystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty());
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));

Au lieu de fournir de la clarté, les méthodes d'usine de la classe Optional ne font que distraire le lecteur. Notez qu'il n'y a qu'un seul paramètre facultatif, mais imaginez en avoir deux ou trois. Oncle Bob ne serait certainement pas fier de ce code 😉

Lorsqu'une méthode peut accepter des paramètres facultatifs, il est préférable d'adopter l'approche bien éprouvée et de concevoir un tel cas en utilisant une surcharge de méthode. Dans l'exemple de la classe SystemMessage, déclarer deux constructeurs distincts est supérieur à l'utilisation de Facultatif.

public SystemMessage(String title, String content) {
    this(title, content, null);
}

public SystemMessage(String title, String content, Attachment attachment) {
    // assigning field values
}

Ce changement rend le code client beaucoup plus simple et plus facile à lire.

SystemMessage withoutAttachment = new SystemMessage("title", "content");
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", attachment);
Gili
la source
10
C'est beaucoup de copier-coller lorsque seulement un ou deux paragraphes sont pertinents.
Garret Wilson
47
Malheureusement, cette explication ne répond pas à la question de savoir quand l'appelant est par exemple en analysant certaines informations peuvent être facultatives. Avec la surcharge de méthode (comme recommandé ci-dessus), le code appelant doit dire: "Est-ce que X est présent? Si oui, j'appellerai la méthode surchargée A. Y a-t-il présent? Je devrai appeler la méthode surchargée B. Si X et Y sont présents, je vais devoir appeler la méthode surchargée C. " Etc. Il peut y avoir une bonne réponse à cela, mais cette explication du «pourquoi» ne le couvre pas.
Garret Wilson
9
En outre, en ce qui concerne les collections, il existe une différence distincte entre une collection vide et aucune collection. Par exemple, un cache. S'agissait-il d'une cache cache? vide facultatif / null. Y a-t-il eu un hit de cache qui se trouve être une collection vide? pleine option / collection.
Ajax
1
@Ajax Je pense que vous avez mal compris l'article. Ils citent un point préconisé par le livre Effective Java , qui dit que lorsqu'un type de retour de méthode est une collection (et non un objet arbitraire comme le ferait un cache), vous devriez privilégier le retour d'une collection vide au lieu de nullou Optional.
Gili
4
Bien sûr, vous devriez le favoriser , mais vous n'avez pas toujours le contrôle sur du code et, dans un sens très réel, vous voudrez peut-être faire la différence entre "il y a une valeur et c'est une collection vide" et "il n'y a pas valeur définie (encore) ". Puisque "magic null" est également une pratique découragée, c'est à vous, le développeur, de choisir la moins mauvaise option. Je préfère vide facultatif pour représenter un cache manquant au lieu d'une collection vide, car la valeur en cache réelle peut être une collection vide.
Ajax
86

Il n'y a presque pas de bonnes raisons de ne pas utiliser Optionalde paramètres. Les arguments contre cela reposent sur des arguments d'autorité (voir Brian Goetz - son argument est que nous ne pouvons pas appliquer des options non nulles) ou que les Optionalarguments peuvent être nuls (essentiellement le même argument). Bien sûr, toute référence en Java peut être nulle, nous devons encourager les règles à être appliquées par le compilateur, pas la mémoire des programmeurs (ce qui est problématique et n'est pas évolutif).

Les langages de programmation fonctionnels encouragent les Optionalparamètres. L'une des meilleures façons de l'utiliser est d'avoir plusieurs paramètres facultatifs et liftM2d'utiliser une fonction en supposant que les paramètres ne sont pas vides et en renvoyant un facultatif (voir http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/ data / Option.html # liftM2-fj.F- ). Java 8 a malheureusement implémenté une bibliothèque très limitée prenant en charge en option.

En tant que programmeurs Java, nous ne devons utiliser null que pour interagir avec les bibliothèques héritées.

Mark Perry
la source
3
Que diriez-vous d'utiliser @Nonnullet @Nullablede javax.annotation à la place? Je les utilise largement dans une bibliothèque que je développe et le support fourni par l'IDE (IntelliJ) est très bon.
Rogério
1
C'est bien, mais vous devez utiliser cette annotation partout et vous avez besoin d'une bibliothèque pour prendre en charge des méthodes telles que map, flatMap / bind, liftM2, séquence, etc.
Mark Perry
14
Les langages de programmation fonctionnels encouragent les paramètres facultatifs. Citation requise. La plupart des fonctions ne devraient pas prendre de facultatif; au lieu de cela, le fardeau de traiter (en utilisant des fonctions appropriées d'ordre supérieur) avec la présence ou l'absence d'une valeur incombe à l'appelant de la fonction en question.
jub0bs
5
Cette. En effet, aucune des réponses n'est suffisamment convaincante, et aucune d'entre elles ne répond à cette question spécifique "pourquoi utiliser les options comme paramètres de méthode est considéré comme une mauvaise pratique alors que l'annotation des paramètres de méthode avec @NonNullest tout à fait correcte si elles servent le même objectif de différentes manières?" Pour moi, il n'y a qu'un seul argument qui a au moins un certain sens: "Facultatif devrait être utilisé pour fournir de meilleures API qui ont un type de retour clair. Cependant pour les arguments de méthode, des fonctions surchargées peuvent être utilisées." - encore, je ne pense pas que ce soit un argument suffisamment solide.
Utku Özdemir
1
Bien sûr, préférable à null, mais ce qui est encore plus préférable, c'est de ne pas avoir besoin de beaucoup de valeurs optionnelles en premier lieu parce qu'un bon design: D
yeoman
12

Ce conseil est une variante de la règle de base "être aussi peu précis que possible en ce qui concerne les entrées et aussi spécifique que possible en ce qui concerne les sorties".

Habituellement, si vous avez une méthode qui prend une valeur non nulle, vous pouvez la mapper sur Optional, donc la version simple est strictement plus spécifique en ce qui concerne les entrées. Cependant, il existe un tas de raisons possibles pour lesquelles vous voudriez Optionalnéanmoins exiger un argument:

  • vous souhaitez que votre fonction soit utilisée conjointement avec une autre API qui renvoie un Optional
  • Votre fonction doit renvoyer autre chose qu'un vide Optionalsi la valeur donnée est vide
  • Vous pensez que Optionalc'est tellement génial que quiconque utilise votre API devrait en être informé ;-)
llogiq
la source
10

Le motif avec Optionalest pour éviter de revenir null . Il est encore parfaitement possible de passer nullà une méthode.

Bien que celles-ci ne soient pas encore vraiment officielles, vous pouvez utiliser des annotations de style JSR-308 pour indiquer si vous acceptez ou non des nullvaleurs dans la fonction. Notez que vous devez disposer du bon outil pour l'identifier et qu'il fournirait plus d'une vérification statique qu'une stratégie d'exécution exécutable, mais cela aiderait.

public int calculateSomething(@NotNull final String p1, @NotNull final String p2) {}
Makoto
la source
Le problème ici est plutôt que @NotNull n'est pas le cas par défaut et doit être annoté explicitement - d'où le passe-partout et des choses que les gens ne feront tout simplement pas à cause de la paresse.
dwegener
1
@dwegener Oui, mais Optionalne résout pas non plus le problème, d'où ma suggestion pour les annotations.
Makoto
2
Il est beaucoup plus difficile d'ignorer Facultatif que d'ignorer une annotation que vous ne remarquerez que si vous lisez les documents / source ou si votre outil peut l'attraper (l'analyse statique ne peut pas toujours déterminer correctement la nullité).
Ajax
Notez que @Nullable ne signifie pas que vous "acceptez" null comme une valeur valide, c'est-à-dire que vous ne lancez aucune exception. Cela peut simplement signifier que vous vous gardez contre ce cas, mais vous lèverez toujours une exception (c'est la sémantique de Findbugs). Vous pouvez donc introduire une ambiguïté avec de telles annotations au lieu de la clarté.
tkruse
Je ne sais pas quelle ambiguïté il y a @ user2986984. "Ne pas gérer null" ne peut que signifier de manière réaliste qu'une exception est levée.
Makoto
7

Cela me semble un peu idiot, mais la seule raison pour laquelle je peux penser est que les arguments d'objet dans les paramètres de méthode sont déjà en quelque sorte facultatifs - ils peuvent être nuls. Par conséquent, forcer quelqu'un à prendre un objet existant et à l'envelopper dans une option est en quelque sorte inutile.

Cela étant dit, enchaîner des méthodes qui prennent / retournent des options est une chose raisonnable à faire, par exemple peut-être monade.

Steve B.
la source
7
Les valeurs et les champs ne peuvent-ils pas être également annulés? OptionalEst-ce donc totalement inutile?
Samuel Edwin Ward
6

Consultez le JavaDoc dans JDK10, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html , une note API est ajoutée:

Remarque sur l'API: facultatif est principalement destiné à être utilisé comme type de retour de méthode lorsqu'il est clairement nécessaire de représenter «aucun résultat» et lorsque l'utilisation de null est susceptible de provoquer des erreurs.

Xiaolong
la source
4

Mon point de vue est que Facultatif devrait être une Monade et ceux-ci ne sont pas concevables en Java.

Dans la programmation fonctionnelle, vous traitez des fonctions d'ordre pur et supérieur qui prennent et composent leurs arguments uniquement en fonction de leur "type de domaine métier". La composition de fonctions qui se nourrissent ou dont le calcul doit être rapporté au monde réel (appelés effets secondaires) nécessite l'application de fonctions qui prennent soin de décompresser automatiquement les valeurs des monades représentant le monde extérieur (État, Configuration, Futures, Peut-être, Soit, Écrivain, etc ...); c'est ce qu'on appelle le levage. Vous pouvez y voir une sorte de séparation des préoccupations.

Mélanger ces deux niveaux d'abstraction ne facilite pas la lisibilité, il vaut donc mieux l'éviter.

Tourbillon
la source
3

Une autre raison d'être prudent lorsque vous passez un Optionalparamètre as est qu'une méthode doit faire une chose ... Si vous passez un Optionalparamètre, vous pourriez préférer faire plus d'une chose, il pourrait être similaire de passer un paramètre booléen.

public void method(Optional<MyClass> param) {
     if(param.isPresent()) {
         //do something
     } else {
         //do some other
     }
 }
Pau
la source
Je pense que ce n'est pas une bonne raison. La même logique pourrait être appliquée pour vérifier si param est nul lorsque non facultatif est utilisé. En fait, ce if(param.isPresent())n'est pas la meilleure approche à utiliser en option, vous pouvez utiliser à la place:param.forEach(() -> {...})
hdkrus
Je n'aime pas non plus l'idée de passer des paramètres nullables. Quoi qu'il en soit, j'ai dit que vous pourriez favoriser, bien sûr, il y a des exceptions que vous pouvez utiliser, c'est à vous de décider, utilisez-le avec prudence, c'est tout. Aucune règle ne s'applique à tous les scénarios.
Pau
2

Accepter Facultatif comme paramètres provoque un habillage inutile au niveau de l'appelant.

Par exemple dans le cas de:

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {}

Supposons que vous ayez deux chaînes non nulles (c'est-à-dire renvoyées par une autre méthode):

String p1 = "p1"; 
String p2 = "p2";

Vous êtes obligé de les envelopper en option même si vous savez qu'ils ne sont pas vides.

Cela devient encore pire lorsque vous devez composer avec d'autres structures "mappables", c'est-à-dire. Eithers :

Either<Error, String> value = compute().right().map((s) -> calculateSomething(
< here you have to wrap the parameter in a Optional even if you know it's a 
  string >));

réf:

les méthodes ne doivent pas attendre Option comme paramètres, c'est presque toujours une odeur de code qui indique une fuite du flux de contrôle de l'appelant vers l'appelé, il devrait être de la responsabilité de l'appelant de vérifier le contenu d'une Option

réf. https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749

gpilotino
la source
1

Je pense que c'est parce que vous écrivez habituellement vos fonctions pour manipuler des données, puis que vous les Optionalutilisez pour utiliser mapdes fonctions similaires. Cela lui ajoute le Optionalcomportement par défaut . Bien sûr, il peut y avoir des cas où il est nécessaire d'écrire votre propre fonction auxiliaire qui fonctionne Optional.

Danil Gaponov
la source
1

Je crois que la raison d'être est que vous devez d'abord vérifier si Optional est nul lui-même et ensuite essayer d'évaluer la valeur qu'il englobe. Trop de validations inutiles.

Macchiatow
la source
2
Passer une référence null facultative peut être considéré comme une erreur de programmation (car Facultatif fournit un moyen explicite de passer une valeur "vide"), donc cela ne vaut pas la peine d'être vérifié, sauf si vous voulez éviter de lever des exceptions dans tous les cas.
pvgoran
1

Les options ne sont pas conçues à cet effet, comme l'explique joliment Brian Goetz .

Vous pouvez toujours utiliser @Nullable pour indiquer qu'un argument de méthode peut être nul. L'utilisation d'une option ne vous permet pas vraiment d'écrire plus précisément votre logique de méthode.

Kieran
la source
5
Je suis désolé, mais je ne suis pas d'accord avec l'argument "n'a pas été conçu" ou "quelqu'un le recommande". Un ingénieur, nous devons être précis. L'utilisation de @Nullable est bien pire que l'utilisation d'Optional, car les Optionals sont beaucoup plus verbeux du point de vue de l'API. Je ne vois aucune bonne raison contre l'utilisation d'Optionals comme arguments (à condition que vous ne vouliez pas utiliser la surcharge de méthode)
Kacper86
0

Une autre approche, ce que vous pouvez faire est

// get your optionals first
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

// bind values to a function
Supplier<Integer> calculatedValueSupplier = () -> { // your logic here using both optional as state}

Une fois que vous avez construit une fonction (fournisseur dans ce cas), vous pourrez la passer comme n'importe quelle autre variable et vous pourrez l'appeler en utilisant

calculatedValueSupplier.apply();

L'idée ici étant que vous ayez ou non une valeur optionnelle sera un détail interne de votre fonction et ne sera pas en paramètre. Penser les fonctions lorsque l'on pense à l'option comme paramètre est en fait une technique très utile que j'ai trouvée.

Quant à votre question de savoir si vous devez le faire ou non, cela dépend de vos préférences, mais comme d'autres l'ont dit, cela rend votre API laide pour le moins.

Swaraj Yadav
la source
0

Au début, j'ai également préféré passer les options comme paramètre, mais si vous passez d'une perspective API-Designer à une perspective API-User, vous voyez les inconvénients.

Pour votre exemple, où chaque paramètre est facultatif, je suggère de changer la méthode de calcul en une propre classe comme suit:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

MyCalculator mc = new MyCalculator();
p1.map(mc::setP1);
p2.map(mc::setP2);
int result = mc.calculate();
Torsten
la source
0

Je sais que cette question concerne davantage l'opinion que les faits concrets. Mais je suis récemment passé d'un développeur .net à un développeur java, donc je n'ai rejoint que récemment le groupe Optional. De plus, je préférerais le déclarer comme un commentaire, mais comme mon niveau de points ne me permet pas de commenter, je suis obligé de mettre cela comme une réponse à la place.

Ce que j'ai fait, qui m'a bien servi en règle générale. Est d'utiliser les options pour les types de retour, et d'utiliser uniquement les options comme paramètres, si j'ai besoin à la fois de la valeur de l'option et que la météo ou non l'option ait une valeur dans la méthode.

Si je ne me soucie que de la valeur, je vérifie isPresent avant d'appeler la méthode, si j'ai une sorte de journalisation ou une logique différente dans la méthode qui dépend si la valeur existe, alors je passerai volontiers l'option.

Blair
la source
0

En effet, nous avons des exigences différentes pour un utilisateur d'API et un développeur d'API.

Un développeur est responsable de fournir une spécification précise et une mise en œuvre correcte. Par conséquent, si le développeur sait déjà qu'un argument est facultatif, l'implémentation doit le traiter correctement, qu'il soit nul ou facultatif. L'API doit être aussi simple que possible pour l'utilisateur, et null est le plus simple.

En revanche, le résultat est transmis du développeur d'API à l'utilisateur. Cependant, la spécification est complète et détaillée, il y a toujours une chance que l'utilisateur ne soit pas au courant ou simplement paresseux pour y faire face. Dans ce cas, le résultat Facultatif oblige l'utilisateur à écrire du code supplémentaire pour gérer un éventuel résultat vide.

speedogoo
la source
0

Tout d'abord, si vous utilisez la méthode 3, vous pouvez remplacer ces 14 dernières lignes de code par ceci:

int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

Les quatre variantes que vous avez écrites sont des méthodes pratiques . Vous ne devez les utiliser que lorsqu'ils sont plus pratiques. C'est aussi la meilleure approche. De cette façon, l'API indique très clairement quels membres sont nécessaires et lesquels ne le sont pas. Si vous ne voulez pas écrire quatre méthodes, vous pouvez clarifier les choses par la façon dont vous nommez vos paramètres:

public int calculateSomething(String p1OrNull, BigDecimal p2OrNull)

De cette façon, il est clair que les valeurs nulles sont autorisées.

Votre utilisation de p1.orElse(null)illustre à quel point notre code est verbeux lors de l'utilisation de Facultatif, ce qui explique pourquoi je l'évite. Facultatif a été écrit pour la programmation fonctionnelle. Les streams en ont besoin. Vos méthodes ne devraient probablement jamais retourner Facultatif sauf s'il est nécessaire de les utiliser dans la programmation fonctionnelle. Il existe des méthodes, comme la Optional.flatMap()méthode, qui nécessitent une référence à une fonction qui renvoie Facultatif. Voici sa signature:

public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)

C'est donc généralement la seule bonne raison d'écrire une méthode qui renvoie Facultatif. Mais même là, cela peut être évité. Vous pouvez passer un getter qui ne retourne pas Optional à une méthode comme flatMap (), en l'enveloppant dans une autre méthode qui convertit la fonction en le bon type. La méthode wrapper ressemble à ceci:

public static <T, U> Function<? super T, Optional<U>> optFun(Function<T, U> function) {
    return t -> Optional.ofNullable(function.apply(t));
}

Supposons donc que vous ayez un getter comme celui-ci: String getName()

Vous ne pouvez pas le passer à flatMap comme ceci:

opt.flatMap(Widget::getName) // Won't work!

Mais vous pouvez le passer comme ceci:

opt.flatMap(optFun(Widget::getName)) // Works great!

En dehors de la programmation fonctionnelle, les options doivent être évitées.

Brian Goetz l'a dit le mieux quand il a dit ceci:

La raison pour laquelle Facultatif a été ajouté à Java est la suivante:

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 cela:

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;
MiguelMunoz
la source
1
Eh bien, c'est l'une des raisons pour lesquelles il a été ajouté à Java. Il y en a plein d'autres. Le remplacement de longues chaînes d'instructions «if» imbriquées qui traversent une structure de données avec une seule séquence d'appels chaînés à Optional.map est mon préféré. Le monde de la programmation des langages fonctionnels a de nombreuses utilisations intéressantes pour facultatif en plus de simplement remplacer les contrôles nuls comme celui-ci.
Some Guy