Optional Type
En Java 8 se introdujo una nueva clase llamada java.util.Optional<T>
. Algunas personas asumen que su introducción se dió para eliminar los NullPointerExceptions
del código; pero esa no es su razón de ser.
En realidad, Optional
se diseño para comunicar al usuario cuando un valor que se retorna puede ser legitimamente null.
Dicho esto, una instancia de Optional puede estar en uno de dos estados: una referencia a una instancia del tipo T (conocido como presente
), o vacío conocido como empty
en contraposición a null.
Utilización
Para emplear Optional debemos tomar en cuenta lo siguiente:
- Sus instancias son inmutables (aunque pueden tener referencias a objetos mutables).
- No tiene un constructor público, por lo que debemos instanciarlas por medio de un
factory
- Implementa los métodos
equals
,hashCode
ytoString
Para poder instanciar un Optional, podemos hacer uso de los siguientes métodos:
empty()
: Retorna un Optional vacio.of(T value)
: Retorna un Optional que envuelve el valor que se pasa como parámetro. Nunca le envies un null; pues eso generaría una excepción.ofNullable(T value)
: Retorna un Optional que es vacio si el parámetro es null, o bien envuelve el valor que se para como parámetro.
Observemos este código:
Long valor = null;
Optional<Long> opt = Optional.ofNullable(valor); // Creamos un Optional, pasando un null
System.out.println(opt); // Esto retorna Optional.empty
valor = 8L;
opt = Optional.ofNullable(valor); // Ahora, creamos un optional con el valor 8
System.out.println(opt); // Esto nos diria: Optional[8]
System.out.println(opt.get()); // Retornaria el valor 8 de tipo Long.
Este caso es bastante sencillo; pero podemos mezclarlo con el uso de API de streams para crear cosas como buscar aquellos nombres que tengan más de 6 caracteres.
Optional<String> nombres =
Stream.of("Gerardo", "Marvin", "Kathya", "Rodrigo", "Sergio")
.filter(s -> s.length() > 6)
.findFirst();
System.out.println(nombres.get());
Si corremos este programa; nos retornaría como respuesta Gerardo
; pues es el primer nombre con más de 6 caracteres.
Pero observen que hago uso del método get
; eso no debe hacerse en código productivo. Por qué? Si producto del filter que aplicamos no quedaran datos, la invocación al get nos daría un: java.util.NoSuchElementException: No value present
Una manera de solucionarlo es hacer:
System.out.println(nombres.isPresent() ? nombres.get() : "No hay nombres de esa longitud");
Aquí empleamos el método isPresent
para saber si el Optional esta vacio; y ser así retornar el texto ‘No hay nombres de esa longitud’. Algunos se cuestionaran que sólo cambiamos una evaluación para saber si es null
a un isPresent
y la verdad eso no parece un gran avance.
Y tienen razón, para eso podemos usar otro mecanismo; que sería:
System.out.println(nombres.orElse("No hay nombres de esa longitud"));
Este método orElse
retorna el valor contenido si esta presente o en su lugar devuelve el valor por omisión que proporcionamos. Existen otros métodos similar; que listamos a continuación:
orElseGet(Supplier<? extends T> other)
: Retorna el valor si está presente, en caso contrario invoca el ‘Supplier’ y retorna el resultado.orElseThrow(Supplier<? extends X> exceptionSupplier)
: Retorna el valor si está presente, en caso contrario lanza la excepción creada por el ‘Supplier’
Por ejemplo:
Optional<Cliente> = clientes.stream.findFirst();
clientes.orElse(new Cliente());
clientes.orElseGet(() -> new Cliente());
clientes.orElseThrow(NoSuchElementException::new);
Usando Streams, uno podria aplicar una función a una colección de instancias de Optional, pero solo si tienen un valor presente; a manera de ilustración:
public List<Cliente> buscarClientes(List<Integer> ids) {
return ids.stream()
.map(this::buscarClientePorId)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}
Conclusión
Esperamos que con este artículo les invite a investigar sobre la manera de introducir Optional en sus desarrollos.
Comentarios