В чем разница между этими двумя методами: Optional.flatMap()
и Optional.map()
?
Пример будет оценен.
В чем разница между этими двумя методами: Optional.flatMap()
и Optional.map()
?
Пример будет оценен.
Используйте map
если функция возвращает flatMap
вам объект, или 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);
}
Оба оператора печати печатают одно и то же.
Они оба принимают функцию от необязательного типа к чему-либо.
map()
применяет функцию " как есть " к необязательному:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
Что произойдет, если ваша функция - это функция из T → Optional<U>
?
Ваш результат теперь является Optional<Optional<U>>
!
В этом flatMap()
: если ваша функция уже возвращает Optional
, flatMap()
немного умнее и не flatMap()
его дважды, возвращая Optional<U>
.
Это композиция из двух функциональных идиом: map
и flatten
.
Примечание: - ниже приведена иллюстрация функции map и flatmap, в противном случае опция в первую очередь предназначена для использования только как возвращаемый тип.
Как вы уже знаете, необязательный вариант - это контейнер, который может содержать или не содержать один объект, поэтому его можно использовать везде, где вы ожидаете нулевого значения (вы, возможно, никогда не увидите NPE, если используете опционально). Например, если у вас есть метод, который ожидает объект человека, который может быть нулевым, вы можете написать метод примерно так:
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<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 (если вам нужно) на опцию, не проверяя его isPresent(), если вы не можете жить без NullPointerExceptions.
Что мне помогло, так это взгляд на исходный код двух функций.
Карта - оборачивает результат в необязательный.
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
}
}
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()
, результат будет пустым.