Quelle est la différence entre ces deux méthodes: Optional.flatMap()
et Optional.map()
?
Un exemple serait apprécié.
Utilisez map
si la fonction renvoie l'objet dont vous avez besoin ou flatMap
si la fonction renvoie un Optional
. Par exemple:
public static void main(String[] args) {
Optional<String> s = Optional.of("input");
System.out.println(s.map(Test::getOutput));
System.out.println(s.flatMap(Test::getOutputOpt));
}
static String getOutput(String input) {
return input == null ? null : "output for " + input;
}
static Optional<String> getOutputOpt(String input) {
return input == null ? Optional.empty() : Optional.of("output for " + input);
}
Les deux déclarations d'impression impriment la même chose.
Ils prennent tous deux une fonction du type de l’option à quelque chose.
map()
applique la fonction " telle quelle " sur l'option que vous avez:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
Que se passe-t-il si votre fonction est une fonction de T -> Optional<U>
?
Votre résultat est maintenant un Optional<Optional<U>>
!
C'est ce dont parle flatMap()
: si votre fonction retourne déjà un Optional
, flatMap()
est un peu plus intelligent et ne le double pas, il renvoie Optional<U>
.
C'est la composition de deux idiomes fonctionnels: map
et flatten
.
Remarque: - ci-dessous est l'illustration de la fonction map et flatmap, sinon Optional est principalement conçu pour être utilisé uniquement en tant que type de retour.
Comme vous le savez peut-être déjà, Optional est un type de conteneur pouvant contenir ou non un seul objet. Il peut donc être utilisé partout où vous anticipez une valeur null (vous risquez de ne jamais voir NPE si vous l'utilisez correctement). Par exemple, si vous avez une méthode qui attend un objet personne pouvant être nullable, vous pouvez écrire la méthode à peu près comme ceci:
void doSome(Optional<Person> person){
/*and here you want to retrieve some property phone out of person
you may write something like this:
*/
Optional<String> phone = person.map((p)->p.getPhone());
phone.ifPresent((ph)->dial(ph));
}
class Person{
private String phone;
//setter, getters
}
Ici, vous avez renvoyé un type String qui est automatiquement encapsulé dans un type facultatif.
Si la classe de personnes ressemblait à ceci, le téléphone est également facultatif.
class Person{
private Optional<String> phone;
//setter,getter
}
Dans ce cas, l'appel de la fonction map encapsulera la valeur renvoyée dans Optional et produira quelque chose comme:
Optional<Optional<String>>
//And you may want Optional<String> instead, here comes flatMap
void doSome(Optional<Person> person){
Optional<String> phone = person.flatMap((p)->p.getPhone());
phone.ifPresent((ph)->dial(ph));
}
PS; N'appelez jamais la méthode get (si vous en avez besoin) sur un facultatif sans la cocher avec isPresent (), sauf si vous ne pouvez pas vivre sans NullPointerExceptions.
Ce qui m'a aidé était un coup d'oeil sur le code source des deux fonctions.
Map - encapsule le résultat dans une option.
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value)); //<--- wraps in an optional
}
}
flatMap - retourne l'objet 'raw'
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value)); //<--- returns 'raw' object
}
}
Optional.map()
:Prend chaque élément et si la valeur existe, elle est transmise à la fonction:
Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);
Maintenant ajouté a l'une des trois valeurs suivantes: true
ou false
encapsulé dans un Facultatif , si optionalValue
était présent ou vide Facultatif sinon.
Si vous n'avez pas besoin de traiter le résultat, vous pouvez simplement utiliser ifPresent()
, il n'a pas de valeur de retour:
optionalValue.ifPresent(results::add);
Optional.flatMap()
:Fonctionne de la même manière que les flux. Aplatit le flux de flux. À la différence que si la valeur est présentée, elle est appliquée à la fonction. Sinon, un facultatif vide est renvoyé.
Vous pouvez l'utiliser pour composer des appels de fonctions de valeur facultatives.
Supposons que nous ayons des méthodes:
public static Optional<Double> inverse(Double x) {
return x == 0 ? Optional.empty() : Optional.of(1 / x);
}
public static Optional<Double> squareRoot(Double x) {
return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}
Ensuite, vous pouvez calculer la racine carrée de l'inverse, comme suit:
Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);
ou, si vous préférez:
Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);
Si la inverse()
ou la squareRoot()
renvoie Optional.empty()
, le résultat est vide.