Cómo utilizar Java Optional

La clase Optional fue introducida como parte de Java, y a pesar que para muchos programadores es una forma de remplazar el uso de null y manejar diferente los chequeo de null para las variables, su propósito real es ayudar a introducir la programación funcional en Java. La meta de este artículo es proporcionar algunas buenas prácticas en el uso del Optional en Java.

Creando un Optional en Java

Tenemos 2 métodos para crear una instancia de Optional:

  • Optional.of(value)
  • Optional.ofNullable(value)
Optional<String> myString1 = Optional.of("Hello world!"); //populated optional
Optional<String> myString2 = Optional.of(null); //Throws an NullPointer Exception
Optional<String> myString3 = Optional.ofNullable("Hello world!"); //populated optional
Optional<String> myString4 = Optional.ofNullable(null); //empty optional

Vamos a utilizar Optional.ofNullable siempre que sea posible por que el va a manejar correctamente cualquier valor null. Como podemos ver en el código anterior, usando Optional.of(null) va a lanzar un NullPointerException, por lo tanto es mejor usarlo solamente cuando estamos 100% seguros que el valor nunca será null.

isPresent() y isEmpty()

Podemos usar los métodos isPresent y isEmpty de la clase Optional para saber si dicha instancia tiene un valor o nó. Pero, eso es exactamente lo que queremos evitar!

  Optional<String> myOptional = doSomething();
  String myValue;
  if (myOptional.isPresent()) {
    myValue = myOptional.get();
  } else {
    myValue = null;
  }

No hay mayor diferencia entre usar el chequeo de null (var != null) a utilizar los métoos isPresent o isEmpty. No estamos sacando ventaje de los beneficios del Optional. De hecho, nuestro código luce incluso más grande. Si reealmente queremos mejorar nuestro código debemos de cambiar nuestra forma de pensar y acercarnos a la programación funcional.

Optional y el estilo de programación funcional

Vamos a ver como la mayor parte de nuestro código usando Optional puede ser mejorado usando métodos como: map, filter, getOrElse, getOrElseThrow, ifPresentOrElse, reducinedo el uso de los métodos isEmpty y isPresental minimo, y al mismo tiempo, haciendo nuestro código más sencillo.

Por ejemplo, en lugar de utilizar la declaración if(optional.isPresent), podemos reemplazarlo con el método ifPresentOrElse or con ifPresent, para ejecutar la lógica.

  String myValue = null;
  Optional<String> myOptional = doSomething();
  myOptional.ifPresent(foo -> { doMoreThings(foo);/** more code here **/});
  Car myCar;
  Optional<Car> myOptionalCar = doSomethingCar();
 
  myOptional.ifPresentOrElse( 
    foo -> { /** more code here **/ },
    () -> { /** more code here **/ }
  );

Algunas veces, no necesitamos ejecutar una lógica pero si obtener el valor de una manera segura y almacenarla en alguna variable. En esos casos, podemos usar el método orElse(…)

Optional<Foo> possibleFoo = doSomething();
Foo foo = possibleFoo.orElse(new Foo());

O, hacerlo de una manera “lazy” donde podemos colocar código para crear el resultado del else usando el método orElseGet(…).

Optional<Foo> possibleFoo = doSomething();
Foo foo = possibleFoo.orElseGet(() -> {/**Some logic that returns a Foo**/});

Y que tal si necesitamos lanzar una Excepción cuando el Optional es vacio, por ejemplo, buscando un registro en la base de datos. Para esos casos tenemos el método orElseThrow(()-> {...}).

Optional<User> maybeUser = findUserById(userId);
User user = maybeUser.orElseThrow(() -> new NotFoundException("User not found"));
//If user is present we continue here.

Otros métodos importantes de la clase Optional que podemos usar son filter y map. El map es usado para transformar el resultado en otro objeto, una ventaja es que el map no será llamado si el Optional está vacio.

Optional<User> maybeUser = findUserById(userId);
String userName = maybeUser.map(u -> user.getName()+ " " + user.getLastname())
  .getOrElseThrow(() -> new NotFoundException("User not found"));

Podemos usar también el método filter para reducir el resultado. Si este resultado no comple con la condición del filter retornará un Optional vació.

Optional<User> maybeUser = findUserById(userId);
User adultUser = maybeUser.filter(u -> u.getAge() > 18 )
  .getOrElseThrow(() -> new NotFoundException("User is not an adult"));

Finalmente, podemos notar como es posible usar más de un método del Optional en la misma linea, por ejemplo: map -> filter -> map-> getOrElse. Por lo tanto nuestro código requiere menos líneas.

Conclusión, nunca necesitamos de usar los métodos isPresent() o isEmpty(), al mismo timpo nuestro código es más simple, aplicamos estilo de programación funcional. Esta es la verdadera ventaja de usar la clase Optional

Cuando y cuando no usar el Optional

Como hemos visto hasta el momento la clase Optional is excelente para ser usada como un tipo de retorno (ese es su proposito original), pero que podemos decir hacer de usarla en campos/atributos de una clase, o parámetros de un método. Estas son las mejores prácticas:

– Atributos/Campos

En resumen, evitar usar Optional en atributos/campos de una instancia. Porqué?

  • Porque la clase Optional no es Serializable.
  • Porque los campos constituyen parte del estado del objeto.Como una alternativa, podemos create un método accesor (getter) que returne un Optional.
public class MyObject {
   private String myField;
  
   public Optional<String> getMyField(){
     return Optional.ofNullable(myField); 
   }
}

– Parámetros

No hay ninguna restricción en usar un Optional como un parámetro, pero tengamos en mente que ese Optional sigue siendo un Object y cualquier programador podría mandarlo como un null en la llamada de un método.

References

Share

You may also like...

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *