web-dev-qa-db-fra.com

Java 8 - Différence entre Optional.flatmap et Optional.map

Quelle est la différence entre ces deux méthodes: Optional.flatMap() et Optional.map()?

Un exemple serait apprécié.

124
codependent

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.

124
assylias

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.

47
Diego Martinoia

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.

6
SandeepGodara

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
    }
}
6
Robert Niestroj
  • 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.

2
nazar_art