¿Cuál es la diferencia entre estos dos métodos: Optional.flatMap()
y Optional.map()
?
Un ejemplo sería apreciado.
Stream#flatMap
y Optional#flatMap
.
¿Cuál es la diferencia entre estos dos métodos: Optional.flatMap()
y Optional.map()
?
Un ejemplo sería apreciado.
Stream#flatMap
y Optional#flatMap
.
Respuestas:
Use map
si la función devuelve el objeto que necesita o flatMap
si la función devuelve un 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.
[flat]Map
alguna vez llamaría a la función de mapeo con un input == null
? Tengo entendido que Optional
sortcuts si está ausente: el [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/… ) parece respaldar esto: " Si hay un valor presente, aplique ... . ".
Optional.of(null)
es un Exception
. Optional.ofNullable(null) == Optional.empty()
.
Ambos toman una función del tipo de opcional a algo.
map()
aplica la función " tal cual " en el opcional que tiene:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
¿Qué sucede si su función es una función de T -> Optional<U>
?
Su resultado es ahora 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, regresando Optional<U>
.
Es la composición de dos modismos funcionales: map
y flatten
.
Nota: a continuación se muestra la ilustración del mapa y la función de mapa plano; de lo contrario, Opcional está diseñado principalmente para usarse solo como un tipo de retorno.
Como ya sabrá, Opcional es un tipo de contenedor que puede contener o no un solo objeto, por lo que se puede usar siempre que anticipe un valor nulo (es posible que nunca vea NPE si usa Opcional correctamente). Por ejemplo, si tiene un método que espera que un objeto persona sea anulable, puede escribir el método de la siguiente 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;
//setter, getters
}
Aquí ha devuelto un tipo de cadena que se ajusta automáticamente en un tipo opcional.
Si la clase de persona se ve así, es decir, el teléfono también es opcional
class Person{
private Optional<String> phone;
//setter,getter
}
En este caso, invocar la función de mapa envolverá el valor devuelto en Opcional y producirá algo como:
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));
}
PD; Nunca llame al método get (si es necesario) en un Opcional sin verificarlo con isPresent () a menos que no pueda vivir sin NullPointerExceptions.
Person
está haciendo un mal uso Optional
. Es en contra de la intención de la API usar Optional
en miembros como este: ver mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/…
Lo que me ayudó fue mirar el código fuente de las dos funciones.
Mapa : ajusta el resultado en un Opcional.
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 : devuelve el objeto 'sin procesar'
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
"devuelve el objeto 'crudo'"? flatMap
también devuelve el objeto mapeado "envuelto" en un Optional
. La diferencia es que, en el caso de flatMap
, la función de mapeador envuelve el objeto mapeado Optional
mientras que el map
mismo lo envuelve Optional
.
Optional.map()
:Toma todos los elementos y si el valor existe, se pasa a la función:
Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);
Ahora agregado tiene uno de tres valores: true
o false
envuelto en un Opcional , si optionalValue
estaba presente, o un Opcional vacío de lo contrario.
Si no necesita procesar el resultado, simplemente puede usarlo ifPresent()
, no tiene valor de retorno:
optionalValue.ifPresent(results::add);
Optional.flatMap()
:Funciona de manera similar al mismo método de secuencias. Aplana la corriente de corrientes. Con la diferencia de que si se presenta el valor, se aplica a la función. De lo contrario, se devuelve un opcional vacío.
Puede usarlo para componer llamadas de funciones de valor opcionales.
Supongamos que tenemos métodos:
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));
}
Luego puedes calcular la raíz cuadrada de la inversa, como:
Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);
o, si lo prefieres:
Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);
Si el inverse()
o el squareRoot()
devuelve Optional.empty()
, el resultado está vacío.
Optional<Double>
tipo como tipo de retorno.
Bueno. Usted sólo tendrá que utilizar 'flatMap' cuando estás frente a Opcionales 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, el mapa opcional # devolverá un valor envuelto por un opcional. Es por eso que obtenemos un Opcional anidado - Optional<Optional<Insurance>
. Y en ②, queremos mapearlo como una instancia de Seguros, así es como sucedió la tragedia. La raíz está anidada. Opcionales. Si podemos obtener el valor central independientemente de los shells, lo haremos. 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 el Java 8 en acción si deseas estudiar Java8 sistemáticamente.