J'ajoute cette deuxième réponse basée sur une modification proposée par l'utilisateur srborlongan à mon autre réponse . Je pense que la technique proposée était intéressante, mais elle ne convenait pas vraiment comme modification de ma réponse. D'autres ont accepté et la modification proposée a été rejetée. (Je ne faisais pas partie des électeurs.) Cependant, la technique a du mérite. Il aurait été préférable que srborlongan ait affiché sa propre réponse. Cela ne s'est pas encore produit, et je ne voulais pas que la technique soit perdue dans les brumes de l'historique des modifications rejetées par StackOverflow, j'ai donc décidé de le faire apparaître comme une réponse distincte moi-même.
Fondamentalement, la technique consiste à utiliser certaines des Optional
méthodes de manière intelligente pour éviter d'avoir à utiliser un opérateur ternaire ( ? :
) ou une instruction if / else.
Mon exemple en ligne serait réécrit de cette façon:
Optional<Other> result =
things.stream()
.map(this::resolve)
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.findFirst();
Un mon exemple qui utilise une méthode d'assistance serait réécrit de cette façon:
/**
* Turns an Optional<T> into a Stream<T> of length zero or one depending upon
* whether a value is present.
*/
static <T> Stream<T> streamopt(Optional<T> opt) {
return opt.map(Stream::of)
.orElseGet(Stream::empty);
}
Optional<Other> result =
things.stream()
.flatMap(t -> streamopt(resolve(t)))
.findFirst();
COMMENTAIRE
Comparons directement les versions originales et modifiées:
// original
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
// modified
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
L'original est une approche simple mais professionnelle: nous obtenons un Optional<Other>
; s'il a une valeur, nous renvoyons un flux contenant cette valeur, et s'il n'a pas de valeur, nous renvoyons un flux vide. Assez simple et facile à expliquer.
La modification est astucieuse et présente l'avantage d'éviter les conditionnels. (Je sais que certaines personnes n'aiment pas l'opérateur ternaire. S'il est mal utilisé, il peut en effet rendre le code difficile à comprendre.) Cependant, parfois les choses peuvent être trop intelligentes. Le code modifié commence également par un Optional<Other>
. Ensuite, il appelle Optional.map
ce qui est défini comme suit:
Si une valeur est présente, appliquez-lui la fonction de mappage fournie et, si le résultat n'est pas nul, renvoyez un Facultatif décrivant le résultat. Sinon, renvoyez un Facultatif vide.
L' map(Stream::of)
appel renvoie un Optional<Stream<Other>>
. Si une valeur était présente dans l'entrée Facultatif, le Facultatif renvoyé contient un Flux qui contient le seul résultat Autre. Mais si la valeur n'était pas présente, le résultat est un Facultatif vide.
Ensuite, l'appel à orElseGet(Stream::empty)
renvoie une valeur de type Stream<Other>
. Si sa valeur d'entrée est présente, il obtient la valeur, qui est l'élément unique Stream<Other>
. Sinon (si la valeur d'entrée est absente), elle renvoie un vide Stream<Other>
. Le résultat est donc correct, le même que le code conditionnel d'origine.
Dans les commentaires sur ma réponse, concernant le montage rejeté, j'avais décrit cette technique comme "plus concise mais aussi plus obscure". Je m'en tiens à cela. Il m'a fallu un certain temps pour comprendre ce qu'il faisait, et il m'a également fallu un certain temps pour rédiger la description ci-dessus de ce qu'il faisait. La subtilité clé est la transformation de Optional<Other>
à Optional<Stream<Other>>
. Une fois que vous avez réfléchi, cela a du sens, mais ce n'était pas évident pour moi.
Je reconnais cependant que les choses initialement obscures peuvent devenir idiomatiques avec le temps. Il se peut que cette technique finisse par être le meilleur moyen dans la pratique, au moins jusqu'à ce qu'elle Optional.stream
soit ajoutée (si jamais elle le fait).
MISE À JOUR: Optional.stream
a été ajouté à JDK 9.
.flatMap(Optional::toStream)
, avec votre version vous voyez réellement ce qui se passe.