¿Cuál es la diferencia entre Opcional.flatMap y Opcional.map?

Resuelto codependent asked hace 9 años • 8 respuestas

¿Cuál es la diferencia entre estos dos métodos? Optional.flatMap()yOptional.map() ?

Se agradecería un ejemplo.

codependent avatar Jun 16 '15 17:06 codependent
Aceptado

Úselo mapsi la función devuelve el objeto que necesita o flatMapsi 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.

assylias avatar Jun 16 '2015 10:06 assylias

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: mapy flatten.

Diego Martinoia avatar Jun 16 '2015 10:06 Diego Martinoia

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.

momonannan avatar Jan 16 '2020 10:01 momonannan

NOTA

A continuación se muestra la ilustración de map()las flatMap()funciones; de lo contrario, Optionalestá diseñado principalmente para usarse únicamente como tipo de devolución.


Como ya sabrá, Optionales un tipo de contenedor que puede contener o no un solo objeto, por lo que puede usarse dondequiera que anticipe un nullvalor (es posible que nunca vea NPE si lo usa Optionalcorrectamente).

Por ejemplo, si tiene un método que espera un Personobjeto 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 Stringtipo que se envuelve automáticamente en un Optionaltipo.

Si Personla clase se viera así (es decir, phonetambién es Optional):

class Person {
    private Optional<String> phone;
    // setters, getters
}

En este caso, invocar map()la función envolverá el valor devuelto Optionaly 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 Optionalsin verificarlo isPresent()a menos que no pueda vivir sin él NullPointerException.

SandeepGodara avatar Mar 15 '2017 19:03 SandeepGodara