Nous avons récemment migré vers Java 8. Maintenant, je vois des applications inondées d’ Optional
objets.
Avant Java 8 (Style 1)
Employee employee = employeeServive.getEmployee();
if(employee!=null){
System.out.println(employee.getId());
}
Après Java 8 (Style 2)
Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
Employee employee = employeeOptional.get();
System.out.println(employee.getId());
}
Je ne vois aucune valeur ajoutée Optional<Employee> employeeOptional = employeeService.getEmployee();
lorsque le service lui-même renvoie facultatif:
Venant d'un environnement Java 6, je vois le style 1 comme plus clair et avec moins de lignes de code. Y at-il un avantage réel qui me manque ici?
Consolidé à partir de la compréhension de toutes les réponses et des recherches plus poussées sur le blog
employeeOptional.isPresent()
semble manquer complètement le point de types d'options. Selon le commentaire de @ MikePartridge, cela n'est ni recommandé ni idiomatique. Vérifier explicitement si un type d'option est quelque chose ou rien est un non-non. Vous êtes censé simplementmap
ouflatMap
sur eux.Optional
de Brian Goetz , architecte des langages Java chez Oracle.Réponses:
Le style 2 ne va pas assez en Java 8 pour voir le plein bénéfice. Vous ne voulez pas du
if ... use
tout. Voir les exemples Oracle . En suivant leurs conseils, nous obtenons:Style 3
Ou un extrait plus long
la source
void ifPresent(Consumer<T>, Runnable)
je plaiderais pour une interdiction totale deisPresent
etget
ifPresentOrElse(Consumer<? super T>, Runnable)
.Si vous utilisez
Optional
une couche de "compatibilité" entre une ancienne API qui peut toujours être renvoyéenull
, il peut être utile de créer l'option Facultatif (non vide) à la dernière étape, afin de vous assurer que vous disposez de quelque chose. Par exemple, où vous avez écrit:J'opterais pour:
Le point est que vous savez qu'il ya un employé non nul de service , de sorte que vous pouvez envelopper que dans un
Optional
avecOptional.of()
. Ensuite, lorsque vous appelezgetEmployee()
à ce sujet, vous pouvez ou non engager un employé. Cet employé peut (ou peut-être pas) avoir un identifiant. Ensuite, si vous vous retrouvez avec un identifiant, vous souhaitez l’imprimer.Il n'y a pas besoin de vérifier explicitement toute null, présence, etc., dans ce code.
la source
(...).ifPresent(aMethod)
dessusif((...).isPresent()) { aMethod(...); }
? Il semble que ce soit simplement une syntaxe supplémentaire pour faire presque la même opération.Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet);
Maintenant, je peux traiterchildren
uniformément, par exemplechildren.forEach(relative::sendGift);
. Ceux -ci pourraient même être combinés:Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);
. Tout le passe-partout de la vérification à zéro, etc., ...Il n’ya pratiquement aucune valeur ajoutée à avoir une
Optional
valeur unique. Comme vous l'avez vu, cela ne fait que remplacer un contrôlenull
par un contrôle de présence.Il est énorme valeur ajoutée à avoir une
Collection
de ces choses. Toutes les méthodes de diffusion en continu introduites pour remplacer les anciennes boucles de bas niveau comprennent lesOptional
choses et font ce qu’il faut faire à leur sujet (ne pas les traiter ou, au final, renvoyer un autre non définiOptional
). Si vous traitez n articles plus souvent qu'avec des articles individuels (et que 99% de tous les programmes réalistes le font), le fait de prendre en charge de manière optionnelle les éléments présents constitue un énorme progrès. Dans le cas idéal, vous ne devez jamais vérifiernull
ni être présent.la source
Employee getEmployee()
vsOptional<Employee> getEmployee()
. Techniquement, les deux pourraient revenirnull
, mais l’un d’eux risque beaucoup plus de causer des ennuis..map()
pas avoir à vérifier null tout au long du chemin est génial. P. exOptional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);
.null
.Tant que vous utilisez
Optional
comme une API sophistiquéeisNotNull()
, oui, vous ne trouverez aucune différence avec la simple vérificationnull
.Choses que vous ne devriez PAS utiliser
Optional
pourEn utilisant
Optional
juste pour vérifier la présence d'une valeur est mauvais code ™:Les choses que vous devriez utiliser
Optional
pourEvitez qu'une valeur ne soit pas présente, en en produisant une lorsque cela est nécessaire lors de l'utilisation de méthodes d'API à renvoi nul:
Ou, si créer un nouvel employé coûte trop cher:
De plus, sachez à tout le monde que vos méthodes risquent de ne pas renvoyer de valeur (si, pour une raison quelconque, vous ne pouvez pas renvoyer une valeur par défaut comme ci-dessus):
Et enfin, il y a toutes les méthodes de type flux qui ont déjà été expliquées dans d'autres réponses, même si ce ne sont pas vraiment vous qui décidez d'utiliser,
Optional
mais vous utilisez les informationsOptional
fournies par quelqu'un d'autre.la source
Optional
une occasion manquée. "Ici, j'ai cette nouvelleOptional
chose que j'ai faite donc vous êtes obligé de vérifier les valeurs nulles. Oh, et au fait ... j'ai ajouté uneget()
méthode pour que vous n'ayez pas à vérifier quoi que ce soit;);)" . (...)Optional
est agréable comme un signe au néon "CECI POURRAIT ÊTRE NUL", mais la simple existence de saget()
méthode le paralyse " . Mais OP demandait des raisons d'utilisationOptional
, pas des raisons de s'y opposer ni des défauts de conception.Employee employee = Optional.ofNullable(employeeService.getEmployee());
Dans votre troisième extrait, le type ne devrait-il pas êtreOptional<Employee>
?get
est si vous devez interagir avec un code hérité. Je ne peux pas penser à de nombreuses raisons valables d'utilisation du nouveau codeget
. Cela dit, quand on interagit avec le code existant, il n’ya souvent aucune alternative, mais il n’en reste pas moins que lesget
débutants sont capables d’écrire du mauvais code.Map
une seule fois et je ne suis au courant de personne ayant effectué un changement aussi radicalement non compatible avec les versions antérieures , alors assurez-vous que vous pouvez intentionnellement concevoir de mauvaises API avecOptional
- ne savez pas ce que cela prouve. UnOptional
serait logique pour unetryGet
méthode cependant.Map
est un exemple connu de tous. Bien sûr, ils ne peuvent pas faire en sorte queMap.get
Return soit facultatif en raison de la compatibilité ascendante, mais vous pouvez facilement imaginer une autre classe semblable à Map qui ne serait pas soumise à de telles contraintes de compatibilité. Disclass UserRepository {Optional<User> getUserDetails(int userID) {...}}
. Maintenant, vous voulez obtenir les détails de l'utilisateur connecté et vous savez qu'ils existent parce qu'ils ont réussi à se connecter et que votre système ne supprime jamais les utilisateurs. Alors vous faitestheUserRepository.getUserDetails(loggedInUserID).get()
.Le point essentiel pour moi lors de l'utilisation de Optional est de rester clair lorsque le développeur doit vérifier si la fonction renvoie une valeur ou non.
la source
Votre erreur principale est que vous envisagez toujours de manière plus procédurale. Ceci n'est pas conçu comme une critique de vous en tant que personne, c'est simplement une observation. Penser en termes plus fonctionnels va de pair avec le temps et la pratique. Par conséquent, les méthodes sont actuelles et donnent l’impression que les choses correctes les plus évidentes s’appellent à vous. Votre erreur mineure secondaire est la création de votre option dans votre méthode. Facultatif est conçu pour aider à documenter le fait que quelque chose peut ou non renvoyer une valeur. Vous pourriez ne rien obtenir.
Cela vous a conduit à écrire un code parfaitement lisible qui semble parfaitement raisonnable, mais vous avez été séduit par les viles tentacules jumelles qui se font et sont présentes.
Bien sûr, la question devient rapidement "pourquoi sont-ils présents et même y arriver?"
Une chose qui manque à beaucoup de gens ici, c'est que isPresent () est que ce n'est pas quelque chose qui est fait pour le nouveau code écrit par des gens pleinement conscients de l'utile dieu que sont les lambda, et qui aime le fonctionnel.
Cependant, il nous donne quelques (deux) bons, grands avantages glamour (?):
Le premier est plutôt simple.
Imaginez que vous ayez une API qui ressemble à ceci:
Et vous aviez une classe héritée utilisant ceci en tant que tel (complétez les blancs):
Un exemple artificiel pour être sûr. Mais supporte avec moi ici.
Java 8 est maintenant lancé et nous nous efforçons de monter à bord. L’une des choses que nous faisons est donc de remplacer notre ancienne interface par quelque chose qui retourne facultatif. Pourquoi? Parce que, comme quelqu'un d'autre l'a déjà gracieusement mentionné: Cela élimine la question de savoir si quelque chose peut être nul ou non. Cela a déjà été souligné par d'autres. Mais maintenant nous avons un problème. Imaginez que nous ayons (excusez-moi pendant que je frappe alt + F7 une méthode innocente), 46 endroits où cette méthode est appelée dans un code hérité bien testé qui fait un excellent travail sinon. Maintenant, vous devez mettre à jour tous ces éléments.
C’est là que brille isPresent.
Parce que maintenant: Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {lance la nouvelle NoSuchSnickersException (); } else {consumer.giveDiabetes (lastSnickers); }
devient:
Et ceci est un changement simple que vous pouvez donner au nouveau junior: il peut faire quelque chose d’utile, et il va pouvoir explorer le code en même temps. gagnant-gagnant. Après tout, quelque chose qui ressemble à ce modèle est assez répandu. Et maintenant, vous n'avez pas à réécrire le code pour utiliser des lambdas ou quoi que ce soit. (Dans ce cas particulier, ce serait trivial, mais je laisse penser au lecteur des exemples où ce serait difficile comme exercice.)
Notez que cela signifie que votre façon de faire est essentiellement une façon de traiter le code hérité sans faire de réécriture coûteuse. Alors, qu'en est-il du nouveau code?
Eh bien, dans votre cas, où vous voulez simplement imprimer quelque chose, vous feriez simplement:
snickersCounter.lastConsumedSnickers (). ifPresent (System.out :: println);
Ce qui est assez simple et parfaitement clair. Le point qui remonte lentement à la surface est qu’il existe des cas d’utilisation pour get () et isPresent (). Ils sont là pour vous permettre de modifier mécaniquement le code existant pour utiliser les nouveaux types sans trop y penser. Ce que vous faites est donc malavisé des manières suivantes:
Si vous souhaitez utiliser facultatif comme simple contrôle de sécurité null, vous devez simplement procéder comme suit:
Bien sûr, la belle version de ceci ressemble à ceci:
En passant, bien que cela ne soit absolument pas nécessaire, je recommande d’utiliser une nouvelle ligne par opération, afin de faciliter la lecture. Facile à lire et à comprendre bat la concision tous les jours de la semaine.
Ceci est bien sûr un exemple très simple où il est facile de comprendre tout ce que nous essayons de faire. Ce n'est pas toujours aussi simple dans la vie réelle. Mais remarquez comment dans cet exemple, ce que nous exprimons sont nos intentions. Nous voulons OBTENIR l'employé, OBTENIR son identifiant et, si possible, l'imprimer. C'est la deuxième grande victoire avec Optional. Cela nous permet de créer un code plus clair. Je pense aussi que faire des choses comme créer une méthode qui fait beaucoup de choses pour pouvoir l’alimenter sur une carte est en général une bonne idée.
la source
map(x).ifPresent(f)
n'a tout simplement pas le même sens intuitif que leif(o != null) f(x)
fait. Laif
version est parfaitement claire. C’est un autre cas de personnes qui sautent dans un groupe populaire, et non de véritables progrès.Optional
. Je faisais référence uniquement à l'utilisation de facultatif dans ce cas précis, et plus encore à la lisibilité, puisque plusieurs personnes agissent comme si ce format était tout aussi lisible, voire plus, queif
.isPresent
etget
autant que possible, de façon réaliste) améliorer la sécurité . Il est plus difficile d'écrire un code incorrect qui ne permet pas de vérifier l'absence de valeur si le type estOptional<Whatever>
plutôt que d'utiliser une valeur nullableWhatever
.get
, vous êtes obligé de choisir quoi faire quand une valeur manquante se produit: vous utiliseriezorElse()
(ouorElseGet()
) pour fournir une valeur par défaut, ouorElseThrow()
pour jeter une valeur choisie. exception qui documente la nature du problème , ce qui est mieux qu'un NPE générique qui n'a pas de sens jusqu'à ce que vous creusiez dans la trace de la pile.Je ne vois pas l'avantage dans le style 2. Vous devez toujours comprendre que vous avez besoin du contrôle nul et qu'il est maintenant encore plus volumineux et donc moins lisible.
Le meilleur style serait, si employeeServive.getEmployee () renverrait Facultatif et le code deviendrait alors:
De cette façon, vous ne pouvez pas appeleremployeeServive.getEmployee (). GetId () et vous remarquez que vous devez gérer la valeur optionnelle d'une manière ou d'une autre. Et si, dans l'ensemble de votre méthode, les méthodes de la base de code ne renvoient jamais la valeur null (ou presque jamais avec des exceptions bien connues de votre équipe à cette règle), la sécurité contre NPE sera renforcée.
la source
isPresent()
. Nous utilisons optionnel pour ne pas avoir à tester.isPresent()
. C'est la mauvaise approche pour les optionnels. Utilisezmap
ou à laflatMap
place.ifPresent
) n'est qu'un autre moyen de tester.ifPresent(x)
est tout aussi un test queif(present) {x}
. La valeur de retour n'est pas pertinente.Je pense que le plus grand avantage est que si votre signature de la méthode retourne un
Employee
vous ne cochez NULL. Vous savez par cette signature qu'il est garanti de renvoyer un employé. Vous n'avez pas besoin de gérer un cas d'échec. Avec une option, vous savez que vous faites.Dans le code que j'ai vu, certaines vérifications nulles n'échouent jamais car les utilisateurs ne veulent pas tracer dans le code pour déterminer si une valeur null est possible, ils lancent donc des vérifications nulles partout de manière défensive. Cela rend le code un peu plus lent, mais plus important encore, il le rend beaucoup plus bruyant.
Cependant, pour que cela fonctionne, vous devez appliquer ce motif de manière cohérente.
la source
@Nullable
et@ReturnValuesAreNonnullByDefault
à l’analyse statique.package-info.java
avec@ReturnValuesAreNonnullByDefault
tous mes paquets, donc tout ce que je dois faire est d'ajouter@Nullable
où vous devez passer àOptional
. Cela signifie moins de caractères, pas de déchets ajoutés et pas de temps d’exécution supplémentaire. Je n'ai pas besoin de consulter la documentation telle qu'elle apparaît lorsque vous survolez la méthode. L'outil d'analyse statique détecte les erreurs et les modifications ultérieures de nullabilité.Optional
est que cela ajoute une autre façon de traiter la nullité. Si c'était en Java depuis le tout début, tout irait bien, mais maintenant, c'est très pénible. Aller à la manière de Kotlin serait beaucoup mieux à mon humble avis. +++ Notez qu'ilList<@Nullable String>
s'agit toujours d'une liste de chaînes, alors que ceList<Optional<String>>
n'est pas le cas.Ma réponse est: ne le fais pas. Au moins réfléchir à deux fois si c'est vraiment une amélioration.
Une expression (tirée d'une autre réponse) comme
semble être la bonne façon fonctionnelle de traiter les méthodes de retour initialement nullables. Ça a l'air sympa, ça ne jette jamais et ça ne vous fait jamais penser à gérer le cas "absent".
Il a l'air mieux que la façon à l'ancienne
ce qui montre qu'il peut y avoir des
else
clauses.Le style fonctionnel
orElse
n'est pas toujours suffisant)En aucun cas, il ne devrait être utilisé comme une panacée. S'il était universellement applicable, alors la sémantique de l'
.
opérateur devrait être remplacée par la sémantique de l'?.
opérateur , le NPE oublié et tous les problèmes ignorés.Tout en étant un grand fan de Java, je dois dire que le style fonctionnel n'est qu'un substitut du pauvre homme Java à la déclaration
bien connu d'autres langues. Sommes-nous d'accord pour dire que cette déclaration
la source
employeeService.getEmployee().getId().apply(id => System.out.println(id));
rendre null-safe une fois à l’aide de l’opérateur de navigation Safe et une fois à l’aide deOptional
. Bien sûr, nous pourrions appeler cela un "détail de mise en oeuvre", mais la mise en oeuvre compte. Il y a des cas où les lamdas rendent le code plus simple et plus agréable, mais ici, c'est juste un mauvais abus.Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);
fonction de la langue:employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));
. Deux syntaxes, mais elles finissent par ressembler beaucoup à moi. Et le "concept" estOptional
akaNullable
akaMaybe
+++
Ce qui est maltraité, ce sont les lamdas qui gèrent la nullité. Les Lamdas vont bien, maisOptional
ne le sont pas. La nullabilité nécessite simplement un support linguistique approprié, comme dans Kotlin. Un autre problème:List<Optional<String>>
est sans rapport avecList<String>
, nous aurions besoin qu’ils soient liés.Facultatif est un concept (un type d'ordre supérieur) issu de la programmation fonctionnelle. Son utilisation supprime la vérification de nullité imbriquée et vous sauve de la pyramide du destin.
la source