Java 8 Optional

Java 8 introduce Optional, este es un wrapper que nos ayuda a prevenir los nullpointer tan comunes en Java. Optional nos da un métodos adecuados para validar si el valor contenido esta null o no.

Evitar los Nullpointer es uno de los problemas con los que habitualmente nos encontramos los programadores en Java. Continuamente realizamos operaciones para verificar que el valor no sea null o tener la precaución de devolver valores not null y a pesar de esto es habitual el fallo por los null pointer.

Java 8 Optional

Como crear un Optional para un objeto

La forma correcta de crear un Optional para un objeto no nulo es la siguiente.

User user = new User("gustavo"); 
Optional<User> optUser = Optional.of(user);

Si utilizas Optional.of con un valor null obtendras un nullpointer inmediatamente en el metodo of.

Esto resulta en la ventaja de no esperar hasta usar el User para encontrarse con la excepción.

Por ejemplo, aqui tendremos un null pointer en Optional, antes de hacer uso de User nulo.

User user = null; 
Optional<User> optUser = Optional.of(user); // NullPointerException here
… 
user.getName(); 

// ...
Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Objects.java:203)
    at java.util.Optional.<init>(Optional.java:96)
    at java.util.Optional.of(Optional.java:108)
    at j8.optional.OptionalExample.main(OptionalExample.java:9)

Si sabes que el objeto User está null o es posible que este nulo y quieres devolver un Optional debes utilizar el método Optional.ofNullable(..);

User user = null;
Optional<User> optUser = Optional.ofNullable(user);

Como utilizar Optional en Java

Dijimos que el uso de Optional es determinar si un objeto está nul o no, aunque es mejor decir que Optional te dirá si el objeto está presente.

A continuación vas a crear un Optional para un objeto User y vas a determinar si está presente.

    // logic to create User 
   User user = new User("john");

   // Create Optional for User
   Optional<User> optUser = Optional.ofNullable(user);

   if (optUser.isPresent()) {
       System.out.println(optUser.get().getName());
   }

   // using j8 lambdas
   optUser.ifPresent(u -> System.out.println(u.getName()));

Tambien puedes utilizar lambdas con el metodo ifPresent(..)

User user = new User("john");
   Optional<User> optUser = Optional.ofNullable(user);

   // using j8 lambdas
   optUser.ifPresent(u -> System.out.println(u.getName()));

Como utilizar Optional orElse / orElseGet en Java

Lo que hace orElse es devolver el valor si está presente o el valor que se pasa por parámetro si está ausente. Es decir usa un valor DEFAULT si el objeto no esta presente.

User user = null;
Optional<User> optUser = Optional.ofNullable(user);

System.out.println(optUser.orElse(User.EMPTY_USER).getName());

// output 
// EMPTY_USER

En el caso de orElseGet también se devuelve un valor por default si el valor no está presente, solo que aquí el valor se devuelve utilizando la interfaz funcional Supplier.

Lo positivo aquí es que esta función solo será llamada si el valor no esta presente evitando procesamiento. En nuestro ejemplo es bastante simple, pero en los casos en que la ejecución de la función sea costosa este método es recomendable.

// orElseGet
User user = null;
Optional<User> optUser = Optional.ofNullable(user);

System.out.println(optUser.orElseGet(new Supplier<User>() {
   @Override
   public User get() {
       return User.EMPTY_USER;
   }
}).getName());

// better with j8 lambdas !
System.out.println(optUser.orElseGet(() -> User.EMPTY_USER).getName());

Tirando una expection con Optional.orElseThrow

Vimos que usando orElse / orElseGet lo que buscas es devolver otro valor por default si el valor dentro de Optional está ausente. Usando orElseThrow lo que deseas en cambio es tirar una Exception si este valor no está.

// orElseThrow
User nullUser = null;
Optional<User> optUser = Optional.ofNullable(nullUser);
optUser.orElseThrow(IllegalArgumentException::new);

// output
Exception in thread "main" java.lang.IllegalArgumentException
    at java.util.Optional.orElseThrow(Optional.java:290)
    at j8.optional.OptionalExample.main(OptionalExample.java:35)

Como usar Optional.filter en Java

Optional.filter te permite filtrar elementos que no deseas. Suponiendo que solo deseas imprimir algún resultado para un determinado User, en este ejemplo el User cuyo nombre es “john”.

User user = new User("john");
Optional<User> optUser = Optional.ofNullable(user);

// filter + ifPresent
optUser.filter(u -> "john".equals(u.getName()))
       .ifPresent(u -> System.out.println(u.getName()));

// filter + isPresent
if (optUser.filter(u -> "john".equals(u.getName()))
       .isPresent()) {
   System.out.println("john is present");
}

Como usar Optional.map y Optional.flatMap

Optional.map transforma o ‘mapea’ un valor en otro devolviendote otro Optional para el tipo de dato.

Por ejemplo si deseamos obtener un Optional con la edad Integer) del User

Optional<Integer> userAge = optUser.map(User::getAge);

Ahora siendo el valor devuelto con “Optional.map” también otro Optional podemos aplicarle filtros

// filter + map
if(optUser.map(User::getAge).filter(age -> age >= 18).isPresent()) {
   System.out.print("The user is older");
}

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

Debes usar map si la función te retorna un objeto, en cambio si la función retorna un Optional usas flatMap

// filter + flatMap
optUser.flatMap(User::getOptionalEmail).ifPresent(email -> sendEmail(email));

private static void  sendEmail(String email) {
   System.out.println("Sending email to " + email);
}

Siempre es conveniente utilizar Optional en vez de validar los nulos en la forma tradicional

Optional nos provee de herramientas para evitar los típicos errores con nulos, aunque es necesario usarlo correctamente porque si no validamos antes que el objeto este presente podemos incurrir facilmente en otro tipo de error.

Si olvidamos verificar si el objeto está presente y hacemos uso de get directamente tendremos una exception.

String data = null;
Optional<String> opt = Optional.ofNullable(data);
opt.get().toString();

Exception in thread "main" java.util.NoSuchElementException: No value present
    at java.util.Optional.get(Optional.java:135)

Optional, además de facilitarnos el control de nulos o no presentes nos provee de funcionalidades como filter, map, orElse, orElseThrow que nos resuelven muchas de las tareas habituales.

Puedes consultar este ejemplo en github, espero te sirva.

Hi! If you find my posts helpful, please support me by inviting me for a coffee :)

Ver también