Translate

martes, 3 de septiembre de 2019

Optional, la solución de Java 8 para NullPointerException

Quien no ha sufrido un NullPointerException, no ha programado en Java, a veces en partes del código donde parece imposible que sucedan, posiblemente ese NullPointerException se ha estado gestando desde otras partes lejanas de la aplicación.

Para corregir estos errores, algunos lenguajes han decidido eliminar por completo los temidos null, pero para aquellos lenguajes que en su momento lo incluyeron, no es tan fácil. De ahí la existencia de alternativas cómo el patrón Optional, el cual nos permite mostrar de manera explicita (mediante el sistema de tipos) la posibilidad de que un método pueda no devolver el valor deseado. Esto nos obliga a controlar la posible ausencia de valor de manera explicita, permitiéndonos elegir un valor alternativo en caso de dicha ausencia o simplemente realizar otra acción.

En Java 8 este patrón se encapsula en la clase Optional, la cual incluye muchos de los métodos necesarios para trabajar con este patrón.

public final class Optional<T>{}

Lo más importante es la signatura de la clase, en la cual podemos ver que es una clase genérica que nos permite que el objeto que contenga (o no) sea de cualquier clase.

Optional tiene un constructor privado, y nos proporciona tres métodos factoría estáticos para instanciar la clase. Siendo el método .of el que nos permite recubrir cualquier objeto en un optional.

public static<T> Optional<T> empty()
public static <T> Optional<T> ofNullable(T value)
public static <T> Optional<T> of(T value)

Los otros dos métodos nos permiten recubrir un valor nulo o devolver un objeto Optional vacío en caso de que queramos avisar de la ausencia de valor. La opción de recubrir un nulo viene dada principalmente para permitirnos trabajar con APIs que hacen uso de nulos para avisar de estas ausencias de valor.

El método isPresent es el equivalente a variable == null y cómo el propio nombre indica nos dice si el objeto Optional contiene un valor o está vacío. Este método se usa principalmente si trabajamos de manera imperativa con Optional. Y el método get es el encargado de devolvernos el valor, devolviendo una excepción si no estuviera presente.

  • public Optional<T> filter(Function f)
  • public<U> Optional<U> map(Function f)
  • public<U> Optional<U> flatMap(Function f)


Estos tres métodos hacen que trabajar con Optional sea verdaderamente interesante, nos da la posibilidad de encadenar distintas operaciones que devuelvan Optional sin tener que estar comprobando si el valor está presente después de cada operación.

  • public T orElse(T other)
  • public T orElseGet(Function f)
  • public <X extends Throwable> T orElseThrow(Function f)

Finalmente estos métodos nos permiten finalizar una serie de operaciones, teniendo tres maneras:

  • La primera orElse nos devuelve el valor o si no devolverá el valor que le demos. 
  • orElseGet, nos devolverá el valor si está presente y si no, invocará la función que le pasemos por parámetro y devolverá el resultado de dicha función. 
  • Y finalmente orElseThrow, nos devolverá el valor si está presente y si invocará la función que le pasemos, la cual tiene que devolver una excepción y lanzará dicha excepción. Esto nos ayudará a trabajar en conjunción con APIs que todavía usen excepciones.