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 Optional
s 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 Optional
s 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 Optional
pré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).
null
?Réponses:
Oh, ces styles de codage doivent être pris avec un peu de sel.
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.
la source
Optional
paramètre représente l'un des trois états: unnull
facultatif, un non nulOptional
avecisPresent() == false
et un non nulOptional
encapsulant une valeur réelle.@NotNull Optional
.Optional
du tout, alors l'état 1 (null
facultatif) indique généralement une erreur de programmation, vous pouvez donc aussi ne pas la gérer (laissez-la simplement jeter unNullPointerException
)Le meilleur article que j'ai vu sur le sujet a été écrit par Daniel Olszewski :
la source
null
ouOptional
.Il n'y a presque pas de bonnes raisons de ne pas utiliser
Optional
de 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 lesOptional
arguments 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
Optional
paramètres. L'une des meilleures façons de l'utiliser est d'avoir plusieurs paramètres facultatifs etliftM2
d'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.
la source
@Nonnull
et@Nullable
de 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.@NonNull
est 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.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 voudriezOptional
néanmoins exiger un argument:Optional
Optional
si la valeur donnée est videVous pensez queOptional
c'est tellement génial que quiconque utilise votre API devrait en être informé ;-)la source
Le motif avec
Optional
est pour éviter de revenirnull
. Il est encore parfaitement possible de passernull
à 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
null
valeurs 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.la source
Optional
ne résout pas non plus le problème, d'où ma suggestion pour les annotations.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.
la source
Optional
Est-ce donc totalement inutile?Consultez le JavaDoc dans JDK10, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html , une note API est ajoutée:
la source
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.
la source
Une autre raison d'être prudent lorsque vous passez un
Optional
paramètre as est qu'une méthode doit faire une chose ... Si vous passez unOptional
paramètre, vous pourriez préférer faire plus d'une chose, il pourrait être similaire de passer un paramètre booléen.la source
if(param.isPresent())
n'est pas la meilleure approche à utiliser en option, vous pouvez utiliser à la place:param.forEach(() -> {...})
Accepter Facultatif comme paramètres provoque un habillage inutile au niveau de l'appelant.
Par exemple dans le cas de:
Supposons que vous ayez deux chaînes non nulles (c'est-à-dire renvoyées par une autre méthode):
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 :
réf:
réf. https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749
la source
Je pense que c'est parce que vous écrivez habituellement vos fonctions pour manipuler des données, puis que vous les
Optional
utilisez pour utilisermap
des fonctions similaires. Cela lui ajoute leOptional
comportement par défaut . Bien sûr, il peut y avoir des cas où il est nécessaire d'écrire votre propre fonction auxiliaire qui fonctionneOptional
.la source
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.
la source
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.
la source
Une autre approche, ce que vous pouvez faire est
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
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.
la source
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:
la source
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.
la source
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.
la source
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 laOptional.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:
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:
est plus propre que cela:
la source