В чем разница между этими двумя методами: Optional.flatMap()
а Optional.map()
?
Пример будет оценен.
Stream#flatMap
и Optional#flatMap
.
В чем разница между этими двумя методами: Optional.flatMap()
а Optional.map()
?
Пример будет оценен.
Stream#flatMap
и Optional#flatMap
.
Ответы:
Используйте, map
если функция возвращает нужный вам объект или flatMap
если функция возвращает Optional
. Например:
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);
}
Оба оператора печати печатают одно и то же.
[flat]Map
когда-либо вызывать функцию отображения с input == null
? Optional
Насколько я понимаю, сортировка, если она отсутствует, [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/… ), кажется, подтверждает это: « Если значение присутствует, примените .. . "
Optional.of(null)
это Exception
. Optional.ofNullable(null) == Optional.empty()
,
Они оба принимают функцию от необязательного типа к чему-то.
map()
применяет функцию « как есть » к необязательному:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
Что произойдет, если ваша функция является функцией от T -> Optional<U>
?
Ваш результат теперь Optional<Optional<U>>
!
Вот что flatMap()
значит: если ваша функция уже возвращает an Optional
, flatMap()
она немного умнее и не переносит ее дважды, возвращая Optional<U>
.
Это композиция из двух функциональных идиом: map
и flatten
.
Примечание: - ниже показана иллюстрация функции map и flatmap, в противном случае Optional в первую очередь предназначен для использования только в качестве возвращаемого типа.
Как вы уже знаете, Optional - это вид контейнера, который может содержать или не содержать один объект, поэтому его можно использовать везде, где вы ожидаете нулевое значение (вы никогда не увидите NPE, если используете Optional должным образом). Например, если у вас есть метод, который ожидает объект person, который может быть обнуляемым, вы можете написать метод примерно так:
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
}
Здесь вы вернули тип String, который автоматически переносится в необязательный тип.
Если класс человека выглядел так, то есть телефон также необязательно
class Person{
private Optional<String> phone;
//setter,getter
}
В этом случае вызывающая функция map обернет возвращаемое значение в Optional и выдаст что-то вроде:
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; Никогда не вызывайте метод get (если вам нужно) для Optional, не проверяя его с помощью isPresent (), если вы не можете жить без исключений NullPointerException.
Person
злоупотребляет Optional
. Это противоречит намерению API использовать Optional
таких членов - см. Mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/…
Что мне помогло, так это взгляд на исходный код двух функций.
Карта - оборачивает результат в необязательный.
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 - возвращает необработанный объект
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
«возвращает« необработанный »объект»? flatMap
также возвращает сопоставленный объект, «обернутый» в Optional
. Разница в том, что в случае flatMap
, функция mapper оборачивает сопоставленный объект в то Optional
время как map
сам оборачивает объект Optional
.
Optional.map()
:Принимает каждый элемент и, если значение существует, оно передается функции:
Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);
Добавленный теперь имеет одно из трех значений: true
или false
заключенный в необязательный параметр , если optionalValue
он присутствует, или пустой необязательный в противном случае.
Если вам не нужно обрабатывать результат, который вы можете просто использовать ifPresent()
, у него нет возвращаемого значения:
optionalValue.ifPresent(results::add);
Optional.flatMap()
:Работает аналогично тому же методу потоков. Выравнивает поток потоков. С той разницей, что если значение представлено, оно применяется к функции. В противном случае возвращается пустой необязательный параметр.
Вы можете использовать его для составления необязательных значений функций вызовов.
Предположим, у нас есть методы:
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));
}
Затем вы можете вычислить квадратный корень из обратного, например:
Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);
или, если вы предпочитаете:
Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);
Если какая- либо inverse()
или squareRoot()
возвращается Optional.empty()
, то результат пуст.
Optional<Double>
тип имеет тип возвращаемого значения.
Ладно. Вам нужно использовать «flatMap» только тогда, когда вы сталкиваетесь с вложенными опциями . Вот пример.
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); // ②
}
}
Как и Stream, Optional # map вернет значение, заключенное в Optional. Вот почему мы получаем вложенный Optional - Optional<Optional<Insurance>
. И в ②, мы хотим отобразить это как страховой случай, вот как произошла трагедия. Корень вложенный необязательно. Если мы сможем получить значение ядра независимо от оболочек, мы это сделаем. Это то, что делает flatMap.
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.flatMap(Car::getOptionalInsurance)
.map(Insurance::getName);
}
В конце концов, я настоятельно рекомендую вам Java 8 In Action , если вы хотите систематически изучать Java8.