Après avoir utilisé Java 8 depuis plus de 6 mois environ, je suis assez satisfait des nouvelles modifications de l'API. Un domaine auquel je ne suis toujours pas confiant est le moment de l'utiliser Optional
. Je semble osciller entre vouloir l'utiliser partout où quelque chose peut être null
, et nulle part du tout.
Il semble y avoir beaucoup de situations où je pourrais l'utiliser, et je ne suis jamais sûr s'il ajoute des avantages (lisibilité / sécurité nulle) ou s'il provoque simplement des frais supplémentaires.
Donc, j'ai quelques exemples, et je serais intéressé par les réflexions de la communauté pour savoir si Optional
c'est bénéfique.
1 - En tant que type de retour de méthode publique lorsque la méthode peut retourner null
:
public Optional<Foo> findFoo(String id);
2 - Comme paramètre de méthode lorsque le paramètre peut être null
:
public Foo doSomething(String id, Optional<Bar> barOptional);
3 - En tant que membre facultatif d'un bean:
public class Book {
private List<Pages> pages;
private Optional<Index> index;
}
4 - Dans Collections
:
En général, je ne pense pas:
List<Optional<Foo>>
ajoute quoi que ce soit - d'autant plus que l'on peut utiliser filter()
pour supprimer des null
valeurs, etc., mais y a-t-il de bonnes utilisations Optional
dans les collections?
Des cas que j'ai ratés?
Map<Character, String>
. S'il n'y a pas de substitution je peux utiliser ceci:Optional.ofNullable(map.get(c)).orElse(String.valueOf(c))
. Notez également que Facultatif a été volé à Guava et qu'il a une syntaxe beaucoup plus agréable:Optional.fromNullable(map.get(c)).or(String.valueOf(c));
.filter(Optional::absent)
sortir des "valeurs nulles"getOrDefault()
?Réponses:
Le point principal de
Optional
est de fournir un moyen pour une fonction renvoyant une valeur pour indiquer l'absence d'une valeur de retour. Voir cette discussion . Cela permet à l'appelant de continuer une chaîne d'appels de méthode fluide.Cela correspond le plus étroitement au cas d'utilisation n ° 1 de la question du PO. Cependant, l' absence de valeur est une formulation plus précise que null car quelque chose comme
IntStream.findFirst
cela ne pourrait jamais retourner null.Pour le cas d'utilisation # 2 , en passant un argument facultatif à une méthode, cela pourrait fonctionner, mais c'est plutôt maladroit. Supposons que vous ayez une méthode qui accepte une chaîne suivie d'une deuxième chaîne facultative. Accepter un
Optional
comme deuxième argument entraînerait un code comme celui-ci:Même accepter null est plus agréable:
Le mieux est probablement d'avoir une méthode surchargée qui accepte un seul argument de chaîne et fournit une valeur par défaut pour le second:
Cela a des limites, mais c'est beaucoup plus agréable que ce qui précède.
Les cas d'utilisation # 3 et # 4 , ayant un
Optional
dans un champ de classe ou dans une structure de données, sont considérés comme une mauvaise utilisation de l'API. Tout d'abord, cela va à l'encontre de l'objectif principal de conception,Optional
comme indiqué en haut. Deuxièmement, cela n'ajoute aucune valeur.Il existe trois façons de gérer l'absence d'une valeur dans un
Optional
: pour fournir une valeur de remplacement, pour appeler une fonction pour fournir une valeur de remplacement ou pour lever une exception. Si vous stockez dans un champ, vous le feriez au moment de l'initialisation ou de l'affectation. Si vous ajoutez des valeurs dans une liste, comme l'OP l'a mentionné, vous avez le choix supplémentaire de simplement ne pas ajouter la valeur, "aplatissant" ainsi les valeurs absentes.Je suis sûr que quelqu'un pourrait trouver des cas artificiels où il veut vraiment stocker un
Optional
dans un champ ou une collection, mais en général, il vaut mieux éviter de le faire.la source
Optional
un champ en tant que lorsque des choses doivent être faites ou ne pas être faites en présence ou en l'absence d'une valeur.if(foo == null)
interne serait mieux que simplement stocker le terrain en tant queoptional
. Et je ne vois pas pourquoi appeler explicitement engetOptionalFoo()
interne est mieux que simplement stocker le champ en tant queOptional
. De plus, si un champ peut êtrenull
et un champ ne peut pas l'êtrenull
, pourquoi ne pas le communiquer au compilateur en les créantOptional
ou nonOptional
.Optional<>
experts: "ne pas stockerOptional<>
dans un champ". J'essaie vraiment de comprendre le point de cette recommandation. Considérons un arbre DOM où un nœud pourrait avoir un parent ou non. Il semble que dans le cas d'utilisation quiNode.getParent()
reviendraitOptional<Node>
. Suis-je vraiment censé stockernull
et envelopper le résultat à chaque fois? Pourquoi? Qu'est-ce que cette inefficacité supplémentaire m'achète, à part rendre mon code laid?Optional<Node> getParent() { return Optional.ofNullable(parent); }
. Cela semble cher car il alloue un àOptional
chaque fois! Mais si l'appelant le déballe immédiatement, l'objet est extrêmement éphémère et n'est jamais promu hors de l'espace Eden. Il pourrait même être éliminé par l'analyse d'échappement du JIT. La réponseOptional
finale est "cela dépend" comme toujours, mais l'utilisation dans des champs peut potentiellement surcharger l'utilisation de la mémoire et ralentir la traversée de la structure des données. Et enfin, je pense que cela encombre le code, mais c'est une question de goût.Je suis en retard dans le jeu mais pour ce que ça vaut, je veux ajouter mes 2 cents. Ils vont à l'encontre de l' objectif de conception de
Optional
, qui est bien résumé par la réponse de Stuart Marks , mais je suis toujours convaincu de leur validité (évidemment).Utiliser partout en option
En général
J'ai écrit un article de blog
Optional
complet sur l'utilisation, mais cela se résume à ceci:Optional
au lieu denull
Les deux premières exceptions peuvent réduire le surcoût perçu des références d'emballage et de déballage dans
Optional
. Ils sont choisis de telle sorte qu'un nul ne puisse jamais légalement passer une frontière d'une instance à une autre.Notez que cela n'autorisera presque jamais
Optional
s dans les collections, ce qui est presque aussi mauvais quenull
s. Ne le fais pas. ;)Concernant vos questions
Les avantages
Cela réduit la présence de
null
s dans votre base de code, mais ne les supprime pas. Mais ce n'est même pas le point principal. Il existe d'autres avantages importants:Clarifie l'intention
L'utilisation
Optional
exprime clairement que la variable est, eh bien, facultative. Tout lecteur de votre code ou consommateur de votre API sera battu au-dessus de la tête par le fait qu'il pourrait ne rien y avoir et qu'un contrôle est nécessaire avant d'accéder à la valeur.Supprime l'incertitude
Sans
Optional
le sens d'unnull
événement n'est pas clair. Il peut s'agir d'une représentation légale d'un état (voirMap.get
) ou d'une erreur d'implémentation comme une initialisation manquante ou échouée.Cela change radicalement avec l'utilisation persistante de
Optional
. Ici, déjà l'occurrence denull
signifie la présence d'un bug. (Parce que si la valeur avait été manquante, unOptional
aurait été utilisé.) Cela rend le débogage d'une exception de pointeur nul beaucoup plus facile car la question de la signification de celanull
est déjà résolue.Plus de contrôles nuls
Maintenant que rien ne peut
null
plus être , cela peut être appliqué partout. Que ce soit avec des annotations, des assertions ou des vérifications simples, vous n'avez jamais à vous demander si cet argument ou ce type de retour peut être nul. Ça ne peut pas!Désavantages
Bien sûr, il n'y a pas de solution miracle ...
Performance
L'encapsulation de valeurs (en particulier les primitives) dans une instance supplémentaire peut dégrader les performances. Dans les boucles serrées, cela peut devenir perceptible, voire pire.
Notez que le compilateur peut être en mesure de contourner la référence supplémentaire pour les durées de vie de courte durée de
Optional
s. En Java 10 , les types de valeurs peuvent réduire ou supprimer davantage la pénalité.Sérialisation
Optional
n'est pas sérialisable mais une solution de contournement n'est pas trop compliquée.Invariance
En raison de l'invariance des types génériques en Java, certaines opérations deviennent lourdes lorsque le type de valeur réel est poussé dans un argument de type générique. Un exemple est donné ici (voir "Polymorphisme paramétrique") .
la source
List
s. C'est le cas lorsqu'il est important qu'un certain élément se trouve à un certain indice, mais que l'élément peut être présent ou non. Cela est clairement communiqué par unList<Optional<T>>
type. Si d'un autre côté il est seulement important de savoir quels objets qui sont membres d'une certaine collection, alors il n'y a pas de point.Optional
être passé dans une méthode peut l'êtrenull
. Alors qu'avez-vous gagné? Vous avez maintenant deux contrôles à effectuer. Voir stackoverflow.com/a/31923042/650176Optional
(ce qui devrait être le cas si vous êtes prêt à le passer comme arguments), celanull
n'a jamais de valeur légale. Donc, appeler une méthode avec un paramètre explicitenull
est évidemment une erreur.Personnellement, je préfère utiliser l'outil d'inspection de code d'IntelliJ pour l'utiliser
@NotNull
et les@Nullable
vérifier car ce sont en grande partie du temps de compilation (peut avoir des vérifications d'exécution). Cela a une charge inférieure en termes de lisibilité du code et de performances d'exécution. Ce n'est pas aussi rigoureux que d'utiliser facultatif, mais ce manque de rigueur devrait être soutenu par des tests unitaires décents.Cela fonctionne avec Java 5 et pas besoin d'encapsuler et de dérouler les valeurs. (ou créez des objets wrapper)
la source
@Nonnull
- fonctionne avec FindBugs;)@Nonnull @NotNull etc
Je pense que le Guava Facultatif et leur page wiki le disent assez bien:
Optional
ajoute quelques frais généraux, mais je pense que son avantage clair est de rendre explicite qu'un objet pourrait être absent et il oblige les programmeurs à gérer la situation. Cela empêche que quelqu'un oublie le!= null
chèque bien-aimé .En prenant l'exemple de 2 , je pense qu'il est beaucoup plus explicite d'écrire du code:
que
Pour moi, le
Optional
meilleur capture le fait qu'il n'y a pas de carte son présente.Mes 2 ¢ sur vos points:
public Optional<Foo> findFoo(String id);
- Je ne suis pas certain de cela. Peut-être que je retournerais unResult<Foo>
qui pourrait être vide ou contenir unFoo
. C'est un concept similaire, mais pas vraiment unOptional
.public Foo doSomething(String id, Optional<Bar> barOptional);
- Je préférerais @Nullable et une vérification des trouvailles, comme dans la réponse de Peter Lawrey - voir aussi cette discussion .Optional<Index> getIndex()
pour indiquer explicitement que le livre pourrait ne pas avoir d'index.En général, j'essaierais de minimiser le contournement de l'
null
art. (Une fois brûlé ...) Je pense qu'il vaut la peine de trouver les abstractions appropriées et d'indiquer aux autres programmeurs ce qu'une certaine valeur de retour représente réellement.la source
soundcard.ifPresent(System.out::println)
. AppelerisPresent
est sémantiquement la même chose que recherchernull
.if(soundcard != null && soundcard.isPresent())
. Bien que créer une API qui retourne un Facultatif et pourrait également retourner null est quelque chose que j'espère que personne ne fera jamais.À partir du didacticiel Oracle :
la source
Voici un bon article qui montre l'utilité du cas d'utilisation # 1. Là ce code
se transforme en
en utilisant Facultatif comme valeur de retour des méthodes getter respectives .
la source
Voici une utilisation intéressante (je crois) pour ... les tests.
J'ai l'intention de tester fortement un de mes projets et je construis donc des assertions; il y a seulement des choses que je dois vérifier et d'autres non.
Je construis donc des choses à affirmer et j'utilise un assert pour les vérifier, comme ceci:
Dans la classe NodeAssert, je fais ceci:
Ce qui signifie que l'assertion ne se déclenche vraiment que si je veux vérifier l'étiquette!
la source
Optional
La classe vous permet d'éviter d'utilisernull
et de fournir une meilleure alternative:Cela encourage le développeur à vérifier la présence afin d'éviter les captures non détectées
NullPointerException
.L'API est mieux documentée car il est possible de voir où s'attendre aux valeurs qui peuvent être absentes.
Optional
fournit une API pratique pour continuer à travailler avec l'objetisPresent()
:;get()
;orElse()
;orElseGet()
;orElseThrow()
;map()
;filter()
;flatmap()
.De plus, de nombreux frameworks utilisent activement ce type de données et le renvoient de leur API.
la source
En java, ne les utilisez pas sauf si vous êtes accro à la programmation fonctionnelle.
Ils n'ont pas leur place en tant qu'arguments de méthode (je prémesserai quelqu'un un jour vous passera une option nulle nulle, pas seulement une option vide).
Ils ont un sens pour les valeurs de retour, mais ils invitent la classe cliente à continuer d'étirer la chaîne de développement du comportement.
FP et les chaînes ont peu de place dans un langage impératif comme java car il est très difficile à déboguer, pas seulement à lire. Lorsque vous passez à la ligne, vous ne pouvez pas connaître l'état ni l'intention du programme; vous devez intervenir pour le comprendre (dans du code qui n'est souvent pas le vôtre et de nombreux cadres de pile en profondeur malgré les filtres d'étape) et vous devez ajouter de nombreux points d'arrêt pour vous assurer qu'il peut s'arrêter dans le code / lambda que vous avez ajouté, au lieu de simplement parcourir les lignes triviales if / else / call.
Si vous voulez une programmation fonctionnelle, choisissez autre chose que Java et j'espère que vous avez les outils pour le déboguer.
la source
Je ne pense pas que Facultatif soit un substitut général aux méthodes qui retournent potentiellement des valeurs nulles.
L'idée de base est: L'absence d'une valeur ne signifie pas qu'elle est potentiellement disponible à l'avenir. C'est une différence entre findById (-1) et findById (67).
La principale information des options pour l'appelant est qu'il peut ne pas compter sur la valeur donnée, mais qu'elle peut être disponible à un moment donné. Peut-être qu'il disparaîtra à nouveau et reviendra plus tard une fois de plus. C'est comme un interrupteur marche / arrêt. Vous avez la «possibilité» d'allumer ou d'éteindre la lumière. Mais vous n'avez aucune option si vous n'avez pas de lumière à allumer.
Je trouve donc trop compliqué d'introduire des options partout où précédemment null était potentiellement retourné. J'utiliserai toujours null, mais uniquement dans des zones restreintes comme la racine d'un arbre, l'initialisation paresseuse et les méthodes de recherche explicites.
la source
Semble
Optional
est utile que si le type T en option est un type primitif commeint
,long
,char
, etc. Pour les classes « réelles », il n'a pas de sens pour moi que vous pouvez utiliser unenull
valeur de toute façon.Je pense qu'il a été pris d'ici (ou d'un autre concept de langage similaire).
Nullable<T>
En C #, cela a
Nullable<T>
été introduit il y a longtemps pour encapsuler les types de valeur.la source
Optional
en faitjava.util.Optional
.An a une sémantique similaire à une instance non modifiable du modèle de conception Iterator :
Optional
isPresent()
)get()
) s'il fait référence à un objetnext()
méthode).Par conséquent, envisagez de renvoyer ou de passer un
Optional
dans des contextes où vous auriez précédemment envisagé d'utiliser un JavaIterator
.la source
Java SE 8 introduit une nouvelle classe appelée java.util.Optional,
Vous pouvez créer un Facultatif ou un Facultatif vide avec une valeur nulle.
Et voici un Facultatif avec une valeur non nulle:
Faites quelque chose si une valeur est présente
Maintenant que vous disposez d'un objet Facultatif, vous pouvez accéder aux méthodes disponibles pour traiter explicitement la présence ou l'absence de valeurs. Au lieu de devoir vous rappeler de faire une vérification nulle, comme suit:
Vous pouvez utiliser la méthode ifPresent (), comme suit:
la source