Jaka jest różnica między tymi dwiema metodami: Optional.flatMap()i Optional.map()?
Przykład byłby mile widziany.
Stream#flatMapi Optional#flatMap.
Jaka jest różnica między tymi dwiema metodami: Optional.flatMap()i Optional.map()?
Przykład byłby mile widziany.
Stream#flatMapi Optional#flatMap.
Odpowiedzi:
Użyj, mapjeśli funkcja zwraca potrzebny obiekt lub flatMapjeś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]Mapkiedykolwiek wywołałby funkcję mapowania z input == null? Rozumiem, że Optionalskró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: mapi 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.
Personjest nadużywana Optional. Używanie API Optionalna 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”? flatMapzwraca również mapowany obiekt „opakowany” w plik Optional. Różnica polega na tym, że w przypadku flatMapfunkcji mapper opakowuje mapowany obiekt, Optionalpodczas gdy mapsama 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: truelub falsezawinięte w opcjonalne , jeśli optionalValuebył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.