Jaka jest różnica między tymi dwiema metodami: Optional.flatMap()
i Optional.map()
?
Przykład byłby mile widziany.
Stream#flatMap
i Optional#flatMap
.
Jaka jest różnica między tymi dwiema metodami: Optional.flatMap()
i Optional.map()
?
Przykład byłby mile widziany.
Stream#flatMap
i Optional#flatMap
.
Odpowiedzi:
Użyj, map
jeśli funkcja zwraca potrzebny obiekt lub flatMap
jeśli funkcja zwraca Optional
. Na przykład:
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);
}
Obie instrukcje print drukują to samo.
[flat]Map
kiedykolwiek wywołałby funkcję mapowania z input == null
? Rozumiem, że Optional
skróty sortowania, jeśli ich nie ma - [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/… ) wydaje się potwierdzać to - „ Jeśli wartość jest obecna, zastosuj .. . ”.
Optional.of(null)
jest Exception
. Optional.ofNullable(null) == Optional.empty()
.
Obaj przejmują funkcję od typu opcjonalnego do czegoś.
map()
stosuje funkcję „ tak jak jest ” do posiadanej opcji:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
Co się stanie, jeśli twoja funkcja jest funkcją z T -> Optional<U>
?
Twój wynik to teraz Optional<Optional<U>>
!
O to flatMap()
właśnie chodzi: jeśli twoja funkcja już zwraca an Optional
, flatMap()
jest trochę mądrzejsza i nie zawija go podwójnie, zwraca Optional<U>
.
To kompozycja dwóch funkcjonalnych idiomów: map
i flatten
.
Uwaga: - poniżej znajduje się ilustracja mapy i funkcji mapy płaskiej, w przeciwnym razie Opcjonalne jest przeznaczone głównie do użytku wyłącznie jako typ zwracany.
Jak już być może wiesz, Optional to rodzaj kontenera, który może zawierać pojedynczy obiekt lub nie, więc może być używany wszędzie tam, gdzie spodziewasz się wartości null (możesz nigdy nie zobaczyć NPE, jeśli użyjesz opcji Optional prawidłowo). Na przykład, jeśli masz metodę, która oczekuje obiektu osoby, który może mieć wartość null, możesz napisać metodę mniej więcej tak:
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
}
Tutaj zwróciłeś typ String, który jest automatycznie zawijany w typ opcjonalny.
Jeśli klasa osoby wyglądała tak, to znaczy telefon jest również opcjonalny
class Person{
private Optional<String> phone;
//setter,getter
}
W tym przypadku wywołanie funkcji map spowoduje zawinięcie zwróconej wartości w Optional i da coś takiego:
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; Nigdy nie wywołuj metody get (jeśli musisz) na Optional bez sprawdzania jej za pomocą isPresent (), chyba że nie możesz żyć bez NullPointerExceptions.
Person
jest nadużywana Optional
. Używanie API Optional
na takich członkach jest wbrew intencjom API - patrz mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/ ...
Pomogło mi przyjrzenie się kodowi źródłowemu obu funkcji.
Mapa - zawija wynik w opcjonalny.
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 - zwraca obiekt „surowy”
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
„zwraca„ surowy ”obiekt”? flatMap
zwraca również mapowany obiekt „opakowany” w plik Optional
. Różnica polega na tym, że w przypadku flatMap
funkcji mapper opakowuje mapowany obiekt, Optional
podczas gdy map
sama zawija obiekt Optional
.
Optional.map()
:Pobiera każdy element i jeśli wartość istnieje, jest przekazywana do funkcji:
Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);
Teraz dodane ma jedną z trzech wartości: true
lub false
zawinięte w opcjonalne , jeśli optionalValue
było obecne, lub puste opcjonalne, w przeciwnym razie.
Jeśli nie musisz przetwarzać wyniku, którego możesz po prostu użyć ifPresent()
, nie ma on wartości zwracanej:
optionalValue.ifPresent(results::add);
Optional.flatMap()
:Działa podobnie do tej samej metody strumieni. Spłaszcza strumień strumieni. Z tą różnicą, że jeśli wartość jest prezentowana, jest stosowana do funkcji. W przeciwnym razie zwracany jest pusty element opcjonalny.
Możesz go użyć do tworzenia opcjonalnych wywołań funkcji wartości.
Załóżmy, że mamy metody:
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));
}
Następnie możesz obliczyć pierwiastek kwadratowy z odwrotności, na przykład:
Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);
lub jeśli wolisz:
Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);
Jeśli zwraca inverse()
lub , wynik jest pusty.squareRoot()
Optional.empty()
Optional<Double>
typ jako typ zwracany.
W porządku. Musisz używać „flatMap” tylko wtedy, gdy masz do czynienia z zagnieżdżonymi opcjami . Oto przykład.
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); // ②
}
}
Podobnie jak w przypadku Stream, opcjonalna # map zwróci wartość opakowaną przez opcjonalny. Dlatego otrzymujemy zagnieżdżony plik Optional - Optional<Optional<Insurance>
. W ② chcemy odwzorować to jako instancję ubezpieczeniową, tak właśnie doszło do tragedii. Katalog główny jest zagnieżdżony Optionals. Jeśli uda nam się uzyskać podstawową wartość niezależnie od powłok, zrobimy to. To właśnie robi flatMap.
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.flatMap(Car::getOptionalInsurance)
.map(Insurance::getName);
}
W końcu zdecydowanie poleciłem ci Java 8 w działaniu , jeśli chcesz systematycznie uczyć się Java8.