Яка різниця між цими двома методами: 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()
йдеться: якщо ваша функція вже повертає Optional
, flatMap()
трохи розумніша і не подвоює її двічі, повертаючись Optional<U>
.
Це склад двох функціональних ідіом: map
і flatten
.
Примітка: - нижче наведено ілюстрацію функції карти та плоскої карти, інакше необов'язково в першу чергу призначена для використання лише як тип повернення.
Як ви вже знаєте, необов'язково - це такий тип контейнера, який може містити один об'єкт або не може містити його, тому він може бути використаний у будь-якому місці, коли ви очікуєте нульове значення (ви ніколи не побачите 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
}
У цьому випадку виклик функції карти оберне повернене значення в необов'язковий і отримає щось на кшталт:
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.
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» , коли ви зіткнулися з вкладеними OPTIONALS . Ось приклад.
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<Optional<Insurance>
. І в ② ми хочемо відобразити це як страховий екземпляр, ось так сталася трагедія. Корінь вкладений Необов’язково. Якщо ми зможемо отримати основне значення незалежно від оболонок, ми це зробимо. Ось що робить flatMap.
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.flatMap(Car::getOptionalInsurance)
.map(Insurance::getName);
}
Зрештою, я рішуче рекомендував вам Java 8 в дії, якщо ви хочете систематично вивчати Java8.