Quelle est la différence entre ces deux méthodes: Optional.flatMap()
et Optional.map()
?
Un exemple serait apprécié.
Stream#flatMap
et Optional#flatMap
.
Quelle est la différence entre ces deux méthodes: Optional.flatMap()
et Optional.map()
?
Un exemple serait apprécié.
Stream#flatMap
et Optional#flatMap
.
Réponses:
À utiliser 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 instructions d'impression impriment la même chose.
[flat]Map
jamais la fonction de cartographie avec un input == null
? Je crois comprendre que les Optional
coupes de tri s'il est absent - le [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/… ) semble le confirmer - « Si une valeur est présente, appliquez. . ".
Optional.of(null)
est un Exception
. Optional.ofNullable(null) == Optional.empty()
.
Ils prennent tous deux une fonction du type de l'option à quelque chose.
map()
applique la fonction " tel quel " 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 de cela qu'il flatMap()
s'agit: si votre fonction retourne déjà un Optional
, flatMap()
est un peu plus intelligente et ne la double pas, retourne Optional<U>
.
C'est la composition de deux idiomes fonctionnels: map
et flatten
.
Remarque: - ci-dessous se trouve l'illustration de la fonction map et flatmap, sinon Facultatif est principalement conçu pour être utilisé comme type de retour uniquement.
Comme vous le savez peut-être déjà, Facultatif est un type de conteneur qui peut ou non contenir un seul objet, il peut donc être utilisé partout où vous prévoyez une valeur nulle (vous ne verrez peut-être jamais NPE si vous utilisez Facultatif correctement). Par exemple, si vous avez une méthode qui attend un objet person qui peut être nullable, vous pouvez écrire la méthode quelque chose 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 enveloppé dans un type facultatif.
Si la classe de personne 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 retourné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 Optional sans le vérifier avec isPresent () sauf si vous ne pouvez pas vivre sans NullPointerExceptions.
Person
fait un mauvais usage Optional
. Il est contre l'intention de l'API d'utiliser Optional
sur des membres comme celui-ci - voir mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/…
Ce qui m'a aidé, c'est un regard 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 - renvoie l'objet 'brut'
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
}
}
flatMap
"renvoie l'objet" brut ""? flatMap
renvoie également l'objet mappé «enveloppé» dans un fichier Optional
. La différence est que dans le cas de flatMap
, la fonction mapper enveloppe l'objet mappé dans Optional
tandis que map
lui-même enveloppe l'objet Optional
.
Optional.map()
:Prend chaque élément et si la valeur existe, elle est passée à 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 , s'il optionalValue
était présent, ou un facultatif vide dans le cas contraire.
Si vous n'avez pas besoin de traiter le résultat que vous pouvez simplement utiliser ifPresent()
, il n'a pas de valeur de retour:
optionalValue.ifPresent(results::add);
Optional.flatMap()
:Fonctionne de manière similaire à la même méthode de flux. Aplatit le flux de flux. A la différence que si la valeur est présentée, elle est appliquée à la fonction. Sinon, un optionnel 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:
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 le inverse()
ou les squareRoot()
retours Optional.empty()
, le résultat est vide.
Optional<Double>
type comme type de retour.
D'accord. Vous n'avez besoin d'utiliser 'flatMap' que lorsque vous faites face à des options imbriquées . Voici l'exemple.
public class Person {
private Optional<Car> optionalCar;
public Optional<Car> getOptionalCar() {
return optionalCar;
}
}
public class Car {
private Optional<Insurance> optionalInsurance;
public Optional<Insurance> getOptionalInsurance() {
return optionalInsurance;
}
}
public class Insurance {
private String name;
public String getName() {
return name;
}
}
public class Test {
// map cannot deal with nested Optionals
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.map(Car::getOptionalInsurance) // ① leads to a Optional<Optional<Insurance>
.map(Insurance::getName); // ②
}
}
Comme Stream, Optional # map renverra une valeur encapsulée par un Optional. C'est pourquoi nous obtenons une option imbriquée - Optional<Optional<Insurance>
. Et à ②, nous voulons la cartographier comme une instance d'assurance, c'est ainsi que la tragédie s'est produite. La racine est des options imbriquées. Si nous pouvons obtenir la valeur fondamentale indépendamment des coquilles, nous y arriverons. C'est ce que fait flatMap.
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.flatMap(Car::getOptionalInsurance)
.map(Insurance::getName);
}
En fin de compte, je vous recommande vivement Java 8 In Action si vous souhaitez étudier Java8 systématiquement.