¿Cuál es la diferencia entre Opcional.flatMap y Opcional.map?
¿Cuál es la diferencia entre estos dos métodos? Optional.flatMap()
yOptional.map()
?
Se agradecería un ejemplo.
Úselo map
si la función devuelve el objeto que necesita o flatMap
si la función devuelve un archivo Optional
. Por ejemplo:
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);
}
Ambas declaraciones impresas imprimen lo mismo.
Ambos toman una función del tipo de opcional a algo.
map()
aplica la función " tal cual " en el opcional que tienes:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
¿ Qué sucede si tu función es una función de T -> Optional<U>
?
¡ Tu resultado ahora es un Optional<Optional<U>>
!
De eso flatMap()
se trata: si su función ya devuelve un Optional
, flatMap()
es un poco más inteligente y no lo envuelve dos veces, devolviendo Optional<U>
.
Es la composición de dos modismos funcionales: map
y flatten
.
Bueno. Solo necesitas usar 'flatMap' cuando te enfrentas a Options anidados . Aquí está el ejemplo.
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); // ②
}
}
Al igual que Stream, Opcional#map devolverá un valor envuelto por un Opcional. Es por eso que obtenemos un Opcional -- Optional<Optional<Insurance>
. Y en ②, queremos mapearlo como una instancia de Seguros, así fue como ocurrió la tragedia. La raíz son Opcionales anidados. Si podemos obtener el valor central independientemente de los caparazones, lo lograremos. Eso es lo que hace flatMap.
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.flatMap(Car::getOptionalInsurance)
.map(Insurance::getName);
}
Al final, te recomiendo encarecidamente Java 8 en acción si quieres estudiar Java8 de forma sistemática.
NOTA
A continuación se muestra la ilustración de map()
las flatMap()
funciones; de lo contrario, Optional
está diseñado principalmente para usarse únicamente como tipo de devolución.
Como ya sabrá, Optional
es un tipo de contenedor que puede contener o no un solo objeto, por lo que puede usarse dondequiera que anticipe un null
valor (es posible que nunca vea NPE si lo usa Optional
correctamente).
Por ejemplo, si tiene un método que espera un Person
objeto que puede aceptar valores NULL , es posible que desee escribir el método de esta manera:
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;
// setters, getters
}
Aquí ha devuelto un String
tipo que se envuelve automáticamente en un Optional
tipo.
Si Person
la clase se viera así (es decir, phone
también es Optional
):
class Person {
private Optional<String> phone;
// setters, getters
}
En este caso, invocar map()
la función envolverá el valor devuelto Optional
y producirá algo como:
Optional<Optional<String>>
Pero es posible que quieras Optional<String>
hacerlo, así que aquí viene flatMap()
:
void doSome(Optional<Person> person) {
Optional<String> phone = person.flatMap((p) -> p.getPhone());
phone.ifPresent((ph) -> dial(ph));
}
PD
Nunca llame get()
al método (si es necesario) en un Optional
sin verificarlo isPresent()
a menos que no pueda vivir sin él NullPointerException
.