Imperative Or Functional Optionals

Imperative or functional optionals

Usando Optional de manera imperativa

El problema de esto viene cuando queremos ejecutar varias operaciones consecutivas que devuelvan null. Para ilustrar este caso imaginémonos que después de obtener el Album queremos obtener las canciones del album y finalmente obtener la duración total del album.

private static Optional<Double> getDurationOfAlbumWithName(String name) {
    Album album;
    Optional<Album> albumOptional = getAlbum(name);
    if (albumOptional.isPresent()) {
        album = albumOptional.get();
        Optional<List<Track>> tracksOptional = getAlbumTracks(album.getName());
        double duration = 0;
        if (tracksOptional.isPresent()) {
            List<Track> tracks = tracksOptional.get();
            for (Track track : tracks) {
                duration += track.getDuration();
            }
            return Optional.of(duration);
        } else {
            return Optional.empty();
        }
    } else {
        return Optional.empty();
    }

Como podemos observar esto se nos puede ir de las manos muy rápidamente, cada operación sucesiva que hagamos sobre un método que puede devolver un valor vacío se convierte en un nivel más de anidación.

Usando Optional de manera funcional

A continuación, vamos a resolver el mismo problema, pero esta vez usando distintas construcciones de programación funcional

 Optional<Double> getDurationOfAlbumWithName(String name) {
    Optional<Double> duration = getAlbum(name)
            .flatMap((album) -> getAlbumTracks(album.getName()))
            .map((tracks) -> getTracksDuration(tracks));
    return duration;
}

En este caso los metodos flatMap y map si reciben un optional vacío no continuan la concatenación de llamadas y devuelve un Optional.Empty()

Se puede usar el metodo orElse() o orElseGet() para devolver un valor por defecto o ejecutar un consumer (funcion anonima)

private static double getDurationOfAlbumWithName(String name) {
    return getAlbum(name)
            .flatMap((album) -> getAlbumTracks(album.getName()))
            .map((tracks) -> getTracksDuration(tracks))
            .orElse(0.0);
}