Pongamos el caso siguiente, tenemos un método que obtiene un disco a partir de un nombre, puede darse el caso de que no se encuentre ningún disco con ese nombre, sin Optional tendríamos dos opciones:
- Devolver null en caso de que no encontrásemos el disco.
- Lanzar una excepción indicando que no se ha encontrado el disco.
Con Optional se nos abre la tercera opción:
- public Optional<Album> getAlbum(String artistName)
A la hora de usar este método, de la manera imperativa haríamos lo siguiente.
Album album;
Optional<Album> albumOptional = getAlbum("Random Memory Access");
if(albumOptional.isPresent()){
album = albumOptional.get();
}else{
// Avisar al usuario de que no se ha encontrado el album
}
Esto ya es un avance respecto a los null, ya que estamos indicando explícitamente al usuario de la API de que es posible que no se encuentre el album y de que es necesario que actúe en caso de error.
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.
Llegados a este punto podemos pensar en usar excepciones, las cuales al menos nos permiten tener todas las acciones a la misma altura dentro de un try y resolver los distintos errores en el catch. Pero esto no es tan flexible tampoco, es hora de pensar en funcional.