Development

Optionally working with optionals

Publicado por
Luis Benavides
Optionally working with optionals
Escrito por
Luis Benavides
Publicado en
February 27, 2024
Tiempo de lectura
Categoría
Development

¿Posiblemente has trabajado con alguna librería que te devuelve un Optional <T> y te puedes preguntar para qué complicar esto y no retorna sólo el valor? ¿O en qué me beneficia esto?

Bueno la verdad es que trae bastantes ventajas si lo sabemos aprovechar, cómo usarlo, cuándo si y cuando no. Espero pueda transmitirte mis conocimientos de Optional al terminar este artículo.

La clase Optional llegó a Java a partir de la versión 8, en conjunto a las lambdas y a los Streams que van bastante de la mano, si no te manejas mucho con ellos, podrías ver este post .  Por esto para usarlo simplemente necesitas usar la versión 8 e importando el siguiente package:

import java

Objetivo de los Optional

Más que nada es evitar los queridos NullPointerException, para esto te hace evaluar ¿Qué pasaría si lo que estoy llamando viene nulo? Por ejemplo:

NullPointerException

Digamos que este findById no consigue un registro con este id, en este caso debería retornar un nulo, cosa que si no estamos atentos nos traería la exception que comente anteriormente, como en este ejemplo:

 findById

Entonces para este caso tenemos la ventaja de los optionals, cambiando el código a la siguiente manera:

 optionals

Para luego al ser llamado:

nullableValue

Si es un poco mas verboso, cierto, aunque luego te explico como hacerlo en menos líneas, se cumple la función de hacer que el programador que utilice la función este consciente de que puede que venga o no un valor.

 

Como crear estos Optional

 

Para mejor entendimiento de los Optional utilizaremos como ejemplo el calcular un promedio de un arreglo de números.

 

 

Digamos que el arreglo venga vacío, en ese caso no tiene sentido colocar el promedio como 0,porque confundiría en el caso de que realmente el promedio sea 0, para esto el optional tiene la opción de crear un optional vacío, el cual permitiría distinguir entre 0 y sin valor, para esto se utiliza el siguiente método:

optional.empy

Por otro lado, si calculamos el promedio y estamos seguros de que tiene un valor podemos crear un optional del valor, en este caso va a ser un optional de un Double, aunque podría ser de cualquier clase, (Object, String, MyCustomClass...) y para crearlo utilizamos esta función:

 

Optional.of(value);

Llevando a la práctica todo lo de arriba,  en nuestro ejemplo podríamos ver que en caso de que le pasen como parámetro un arreglo vacío, el optional estará vacío, y en caso contrario mandará el valor correspondiente, quedando de la siguiente manera:

Optional<Double>

Existe último método para crear un optional, el cual es mas que nada para evitarnos el tener que hacer algo parecido a esto:

velue null

Donde simplemente escribimos:

Optional.ofNullable

Que funciona exactamente igual que en el ejemplo anterior… Y si en el caso de que estés pensando ¿Qué pasaría si escribo Optional.of(null)? Bueno lanzaría unNullPointerException que es lo primero que estamos evitando… La ironía de la vida ☺.

 

Métodos útiles

 

En el caso de que llamemos un método que nos retorne un optional, o que simplemente hayamos creado nosotros mismo un optional, tiene varios métodos que nos pueden ayudar a manejar nuestro valor evitando cualquier NullPointerException.

 

orElseThrow

 

Una de las ventajas que trae el optional es que funciona con programación funcional, cosa que nos ahorra bastante código, aunque en el ejemplo de ver si el valor existía. O nomás bien era más verboso, esto es porque no se le está sacando todo el provecho a la clase, por lo que este ejemplo:

orElseThrow

Se podría reducir a simplemente esto:

¿Bastante más corto no?  Incluso si usas java 10 o una versión posterior, podrías dejarlo  como:

findById

el cual lanzaría un NoSuchElementException.

orElse u orElseGet

 

En el caso de que no quisiéramos lanzar ninguna exception, podríamos generar un valor por defecto en este caso al usar “orElse” u “orElseGet” podríamos hacerlo, la diferencia entre estos es que el orElse tiene como parámetro el valor, y el orElseGet tiene un Supplier como parámetro:

empy.orElse

que incluso podría cambiarse por esto:

orElseGet

En estos dos casos, podríamos decir que: ‘si no existe un valor entrégame 0’ o también generar un valor random.

 

Hay que tomar en cuenta que con estos tres métodos orElse, orElseGet, orElseThrow si el valor existe no pasaría lo que se genera en esta parte del código, ejemplo:

Optional

isPresent

Este caso retorna true en el caso de que tenga un valor

 

isEmpty

A partir de java 11existe este método, y es prácticamente un !isPresent();

 

ifPresent

 

Este es bastante similar al isPresent (ve la diferencia entre la f y la s) pero esta toma como parámetro un Consumer en caso de que el valor existe (puedes verlo como un if sin un else)

Optional.of

Or

 

En caso de que quisieras seguir trabajando con un optional, en caso de que no exista el valor y quisieras darle un valor específico, este método existe a partir de java 9:

optional empy

ifPresentOrElse

Este método también viene a partir de java 9, en este caso tiene 2 parámetros y es prácticamente ver un if else …

someOptional

Comunes con Stream

 

Los optional actúan de manera similar con los métodos de Stream , map, flatMap, filter.

 

Aunque en el caso de flatMap es por si tienes un Optional<Optional<T>> para convertirlo en un Optional<T>

 

Ejemplos:

Optional.of

Stream

Este método existe a partir de java 9, con lo que anteriormente hubiésemos tenido que hacer algo así :

someStream

Buenas Prácticas

 

Como comenté en un principio el objetivo de los Optional es darle entender a los programadores que la función que esta llamando, puede o no traer un valor, y hacernos pensar que hacer en el caso de que no tengamos un valor, dado esto mas que nada el valor ideal para los optional es solo para el return type.

Optional

Pero toma en cuenta que para los parámetros no es como si fuera un valor opcional, además, de que hay que tomar en cuenta que los Optional consumen más recursos que el valor por si solo, por esto es mejor evitarlo cuando no es necesario…

 

Revisar que el valor no sea nulo

 

En el caso de que tengas una variable digamos un String no vale la pena generar un Optional solo para ver si el valor es distinto de nulo, tomando en cuenta que consume más recursos por que el optional es un wrapper del valor, por lo que es como decir que hay un Object dentro de otro, cuando solo validando el null utilizamos un solo valor, además de que puede que a veces sea más difícil de leer:

string null

Aunque digamos que tenemos un objeto con varios objetos anidados, en este caso podría ayudarnos a la lectura y además con los tests unitarios (esto porque evitamos múltiples paths de los tests en los if)

optional.ofNullable

Así nos evitamos un:

 

bigObject

Toma en cuenta que se hicieron varios maps, porque si al tomar “subObjectA” y es nulo al intentar tomar “subObjectB” lanzaría un nullPointer, como en este caso:

 

Optional.ofNullable

 

Este caso ayuda con los tests unitarios dado que los paths para el coverage se reducen de muchos(si ninguno es nulo, si el 3ro es nulo, si el segundo es nulo, si el primero es nulo…) a solo dos, sólo si existe o no el valor

 

Classes

 

En el caso de que escribas una clase que tenga valores opcionales, es mejor que los dejes en nulo, y que retornes en los gets un Optional del valor en este caso tenemos una clase persona, que tiene como atributos el nombre, apellido y sobrenombre los dos primeros son necesarios y el sobrenombre es opcional:

class person

Este es un buen ejemplo de como implementarlo dado que da a entender a los programadores que utilicen esta clase que el nickName es opcional y allá ellos que hacen si no viene el valor.

 

Por otro lado, digamos este ejemplo:

class badPerson

cómo ves el ejemplo es prácticamente igual, aunque en este caso el nickName es un Optional aquí hay 2 cosas que están mal.

 

Primero como comenté anteriormente es más pesado usar Optional en vez de simplemente el valor, por esto declarar el campo (field, Instance variable o como prefieras llamarlo) lo haría mas pesado, digamos que creamos muchas instancias de esta clase, al final va a pesar casi 4 veces más por solo agregar este en vez de dejarlo solo como String.

En el caso del constructor, si quisieras que un valor sea opcional es mejor sobrecargar el constructor (también aplica para cualquier método), así en el caso de que la clase sea muy grande te evitas tener que escribir código innecesario como este:

new BadPerson

O incluso algo peor que nos llevaría a la siguiente regla

new BadPerson

¡Un Optional nunca debe ser nulo!

 

En el caso anterior si no viste por qué “la gente sólo quiere ver el mundo arder” digamos que llamas al getNickName:

 

MyBadPerson

Que un Optional te tire un NullPointerException, un disparo duele menos, darse cuenta de ese error no están simple tampoco porque “los Optional son para evitar los NullPointer y ya agregué un orElse para validar” así que puede que sea la última opción que revises para comprobar el error; así que si necesitas un Optional vacío, simplemente usa Optional.Empty()

 

 

Para terminar, recuerda que el Optional es para hacerle entender a los otros programadores, que el valor que esperan venga o no, siempre tomando en cuenta que un Optional nunca debería ser nulo (no hagas que el mundo arda, sólo tenemos uno ☺)

Descarga nuestro Clever UI KIT 👇

Gracias. Te será enviado un mail confirmando la inscripción
¡Ups! Algo salió mal al enviar el formulario.
Gracias. Por rellenar el formulario
¡Ups! Algo salió mal al enviar el formulario.
Gracias. Te será enviado un mail confirmando la inscripción
¡Ups! Algo salió mal al enviar el formulario.
Gracias. Te será enviado un mail confirmando la inscripción
¡Ups! Algo salió mal al enviar el formulario.
Gracias. Por rellenar el formulario
¡Ups! Algo salió mal al enviar el formulario.
Gracias. Por rellenar el formulario
¡Ups! Algo salió mal al enviar el formulario.

Crea tu propio manual de marca con esta plantilla gratuita.
¡Organiza tus activos de diseño de forma más eficiente!