Java 8 - Unterschied zwischen Optional.flatMap und Optional.map

161

Was ist der Unterschied zwischen diesen beiden Methoden: Optional.flatMap()und Optional.map()?

Ein Beispiel wäre willkommen.

codependent
quelle
5
@AlexisC. Ihr Link bezieht sich auf die Karte und die flatMap von Stream, nicht auf Optional.
Eran
1
@Eran Das spielt keine Rolle. Wenn Sie verstehen, wie map / flatMap funktioniert, ob es sich um einen Stream handelt oder nicht, gilt dies auch für einen optionalen Stream. Wenn der Op verstanden hat, wie es für einen Stream funktioniert, sollte er diese Frage nicht stellen. Das Konzept ist das gleiche.
Alexis C.
2
@AlexisC. Nicht wirklich. Die flatMap von Optional hat wenig mit der flatMap von Stream zu tun.
Eran
1
@Eran Ich spreche über den konzeptionellen Unterschied zwischen einer Karte und einer flatMap. Ich mache keine Eins-zu-Eins-Entsprechung zwischen Stream#flatMapund Optional#flatMap.
Alexis C.

Antworten:

165

Verwenden Sie mapdiese Option, wenn die Funktion das benötigte Objekt zurückgibt oder flatMapwenn die Funktion ein zurückgibt Optional. Beispielsweise:

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);
}

Beide print-Anweisungen drucken dasselbe.

Assylien
quelle
5
Frage: Würde [flat]Mapjemals die Mapping-Funktion mit einem aufrufen input == null? Ich verstehe, dass OptionalSortcuts, wenn sie nicht vorhanden sind - das [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/… ) scheint dies zu unterstützen - " Wenn ein Wert vorhanden ist, wenden Sie an. . ".
Boris die Spinne
1
@ BoristheSpider Optional.of (null)! = Optional.empty ()
Diego Martinoia
14
@DiegoMartinoia Optional.of(null)ist ein Exception. Optional.ofNullable(null) == Optional.empty().
Boris die Spinne
1
@ BoristheSpider ja, du hast recht. Ich habe versucht, auf Ihre Frage zu antworten, aber ich denke, ich habe sie noch unklarer gemacht: Konzeptionell sollte Optional.ofNullable (null) NICHT leer sein, aber in der Praxis wird dies als solche angesehen, und Map / Flatmap werden daher nicht ausgeführt.
Diego Martinoia
1
Ich denke, Eingabe sollte weder in getOutputOpt noch in getOutput
DanyalBurke
55

Beide übernehmen eine Funktion vom Typ des Optionalen bis zu etwas.

map()wendet die Funktion " wie sie ist " auf die Option an, die Sie haben:

if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));

Was passiert, wenn Ihre Funktion eine Funktion von ist T -> Optional<U>?
Ihr Ergebnis ist jetzt ein Optional<Optional<U>>!

Darum geht flatMap()es: Wenn Ihre Funktion bereits eine zurückgibt Optional, flatMap()ist sie etwas intelligenter und umschließt sie nicht doppelt, sondern kehrt zurück Optional<U>.

Es ist die Zusammensetzung zweier funktionaler Redewendungen: mapund flatten.

Diego Martinoia
quelle
7

Hinweis: - Nachfolgend finden Sie eine Abbildung der Karten- und Flatmap-Funktion. Andernfalls ist Optional in erster Linie für die Verwendung als Rückgabetyp vorgesehen.

Wie Sie vielleicht bereits wissen, ist Optional eine Art Container, der ein einzelnes Objekt enthalten kann oder nicht. Daher kann es überall dort verwendet werden, wo Sie einen Nullwert erwarten (NPE wird möglicherweise nie angezeigt, wenn Optional ordnungsgemäß verwendet wird). Wenn Sie beispielsweise eine Methode haben, die ein Personenobjekt erwartet, das möglicherweise nullwertfähig ist, möchten Sie die Methode möglicherweise wie folgt schreiben:

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
}

Hier haben Sie einen String-Typ zurückgegeben, der automatisch in einen optionalen Typ eingeschlossen wird.

Wenn die Personenklasse so aussah, ist das Telefon auch optional

class Person{
  private Optional<String> phone;
  //setter,getter
}

In diesem Fall wird der Aufruf der Map-Funktion den zurückgegebenen Wert in Optional umbrechen und Folgendes ergeben:

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; Rufen Sie niemals die get-Methode (falls erforderlich) für eine Option auf, ohne sie mit isPresent () zu überprüfen, es sei denn, Sie können nicht ohne NullPointerExceptions leben.

SandeepGodara
quelle
1
Ich denke, dieses Beispiel wird wahrscheinlich von der Art Ihrer Antwort ablenken, weil Ihre Klasse Personmissbraucht Optional. Es ist gegen die Absicht der API, sie Optionalfür Mitglieder wie diese zu verwenden - siehe mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/…
8bitjunkie
@ 8bitjunkie Vielen Dank für den Hinweis, es unterscheidet sich von Scala Option ..
SandeepGodara
6

Was mir geholfen hat, war ein Blick auf den Quellcode der beiden Funktionen.

Map - umschließt das Ergebnis mit einem optionalen Ergebnis.

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 - gibt das ' raw' -Objekt zurück

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
    }
}
Robert Niestroj
quelle
1
Was meinst du mit flatMap"gibt das 'rohe' Objekt zurück"? flatMapGibt auch das zugeordnete Objekt zurück, das in ein "eingeschlossen" ist Optional. Der Unterschied besteht darin, dass im Fall von flatMapdie Mapper-Funktion das zugeordnete Objekt in das umschließt, Optionalwährend mapdas Objekt selbst eingeschlossen wird Optional.
Derek Mahar
@DerekMahar hat meine gelöscht, Sie müssen sie nicht erneut veröffentlichen, da Sie Ihren Kommentar richtig bearbeitet haben.
Maxxyme
3
  • Optional.map()::

Nimmt jedes Element und wenn der Wert existiert, wird er an die Funktion übergeben:

Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);

Jetzt hinzugefügt hat einen von drei Werten: trueoder falsein eine Option eingewickelt , falls optionalValuevorhanden, oder eine leere Option , falls nicht vorhanden .

Wenn Sie das Ergebnis, das Sie einfach verwenden können ifPresent(), nicht verarbeiten müssen, hat es keinen Rückgabewert:

optionalValue.ifPresent(results::add); 
  • Optional.flatMap()::

Funktioniert ähnlich wie bei Streams. Glättet den Strom von Strömen. Mit dem Unterschied, dass der Wert, wenn er angezeigt wird, auf die Funktion angewendet wird. Andernfalls wird ein leeres optionales Element zurückgegeben.

Sie können es zum Erstellen optionaler Wertfunktionsaufrufe verwenden.

Angenommen, wir haben Methoden:

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));
}

Dann können Sie die Quadratwurzel der Inversen berechnen, wie:

Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);

oder, wenn Sie es vorziehen:

Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);

Wenn entweder das inverse()oder das squareRoot()zurückgegeben wird Optional.empty(), ist das Ergebnis leer.

nazar_art
quelle
1
Dies wird nicht kompiliert. Beide Ausdrücke geben ein optionales <Double> anstelle des Double zurück, dem Sie das Ergebnis zuweisen.
JL_SO
@JL_SO du hast recht. Weil inverse den Optional<Double>Typ als Rückgabetyp hat.
Nazar_art
3

In Ordnung. Sie müssen 'flatMap' nur verwenden, wenn Sie mit verschachtelten Optionals konfrontiert sind . Hier ist das Beispiel.

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);       // ②
    }

}

Optional # map gibt wie Stream einen Wert zurück, der von Optional umschlossen wird. Deshalb erhalten wir eine verschachtelte Option - Optional<Optional<Insurance>. Und bei ② wollen wir es als Versicherungsinstanz abbilden, so geschah die Tragödie. Die Wurzel ist verschachtelte Optionals. Wenn wir den Kernwert unabhängig von den Muscheln erhalten können, werden wir es schaffen. Das macht flatMap.

public Optional<String> getCarInsuranceName(Person person) {
    return person.getOptionalCar()
                 .flatMap(Car::getOptionalInsurance)
                 .map(Insurance::getName);
}

Am Ende habe ich Ihnen Java 8 In Action dringend empfohlen , wenn Sie Java8 systematisch lernen möchten.

Momonannan
quelle