¿Qué es opcional?
El parámetro opcional se utiliza para transportar objetos y permitir que varias API manejen referencias nulas . Veamos el fragmento de código:Coffee coffee = new Coffee();
Integer quantity = coffee.getSugar().getQuantity();
Tenemos una instancia de Coffee en la que obtenemos algo de azúcar de una instancia del objeto Sugar . Si asumimos que el valor de cantidad nunca se estableció en el constructor Coffee , entonces coffee.getSugar().getQuantity() devolverá una NullPointerException . Por supuesto, siempre podemos utilizar las antiguas comprobaciones nulas para solucionar el problema.
Coffee coffee = new Coffee();
Integer quantity = 0;
if (coffee.getSugar() != null) {
quantity = coffee.getSugar().getQuantity();
}
Ahora todo parece estar bien. Pero al escribir código Java, será mejor que evitemos implementar comprobaciones nulas . Veamos cómo se puede hacer esto usando Opcional.
Cómo crear opcional
Hay tres formas de crear objetos opcionales:-
of(valor T) : creación de instancias de un objeto opcional no nulo. Tenga en cuenta que el uso de of() para hacer referencia a un objeto nulo generará una NullPointerException .
-
ofNullable(valor T) : crea un valor opcional para un objeto que puede ser nulo.
-
vacío() : crea una instancia opcional que representa una referencia a nulo .
// пример использования Optional.of(T Value)
String name = "foo";
Optional<String> stringExample = Optional.of(name)
// пример использования Optional.ofNullable(T Value)
Integer age = null;
Optional<Integer> integerExample= Optional.ofNullable(age)
// пример использования Optional.empty()
Optional<Object> emptyExample = Optional.empty();
Entonces tienes un objeto opcional. Ahora echemos un vistazo a los dos métodos principales de Opcional:
-
isPresent() : este método le indica si el objeto opcional contiene un valor no nulo.
-
get() : recupera el valor de Opcional con el valor actual. Tenga en cuenta que llamar a get() en un Opcional vacío dará como resultado una NullPointerException .
Mejora de la verificación nula con opcional
Entonces, ¿cómo podemos mejorar el código anterior? Con Opcional podemos entender la presencia de un objeto usando isPresent() y recuperarlo usando get() . Comencemos empaquetando el resultado de coffee.getSugar() con Opcional y usando el método isPresent() . Esto nos ayudará a determinar si getSugar() devuelve nulo.Coffee coffee = new Coffee();
Optional<String> sugar = Optional.ofNullable(coffee.getSugar());
int quantity = 0;
if (sugar.isPresent()) {
Sugar sugar = sugar.get();
int quantity = sugar.getQuantity();
}
Al observar este ejemplo, empaquetar el resultado de coffee.getSugar() en Opcional no parece agregar ningún valor, sino que agrega molestias. Podemos mejorar el resultado usando las que considero mis funciones favoritas de la clase Opcional:
-
map(Función<? super T,? extiende U> mapper) : asigna el valor contenido en Opcional a la función proporcionada. Si el parámetro Opcional está vacío, entonces map() devolverá Opcional.empty() .
-
orElse(T other) es una versión "especial" del método get() . Puede obtener el valor contenido en Opcional. Sin embargo, en el caso de un Opcional vacío, esto devolverá el valor pasado al método orElse() .
Coffee coffee = new Coffee();
Integer quantity = Optional.ofNullable(coffee.getSugar())
.map(it -> it.getQuantity())
.orElse(0);
Esto es realmente genial, al menos eso creo. Ahora, si en el caso de un valor vacío no queremos devolver el valor predeterminado, entonces debemos lanzar algún tipo de excepción. orElseThrow(Proveedor<? extiende X> excepciónProveedor) devuelve el valor contenido en los parámetros opcionales o genera una excepción si el Opcional está vacío.
Coffee coffee = new Coffee();
Integer quantity = Optional.ofNullable(coffee.getSugar())
.map(it -> it.getQuantity())
.orElseThrow(IllegalArgumentException::new);
Como puede ver, Opcional ofrece varias ventajas:
- comprobaciones nulas de resúmenes
- proporciona una API para manejar objetos nulos
- Permite que el enfoque declarativo exprese lo que se está logrando.
Cómo ser efectivo con Opcional
En mi trabajo, uso Opcional como tipo de retorno cuando un método puede devolver un estado "sin resultado". Normalmente lo uso cuando defino tipos de retorno para métodos.Optional<Coffee> findByName(String name) {
...
}
A veces esto no es necesario. Por ejemplo, si tengo un método que devuelve un int , como getQuantity() en la clase Sugar , entonces el método podría devolver 0 si el resultado es nulo para representar "sin cantidad". Ahora bien, sabiendo esto, podemos pensar que el parámetro Azúcar en la clase Café se puede representar como Opcional. A primera vista, esto parece una buena idea ya que, en teoría, no es necesario que el azúcar esté presente en el café. Sin embargo, aquí es donde me gustaría abordar cuándo no utilizar Opcional. Deberíamos evitar el uso de Opcional en los siguientes escenarios:
-
Como tipos de parámetros para POJO , como DTO . Los opcionales no son serializables, por lo que usarlos en un POJO hace que el objeto no sea serializable.
-
Como argumento de método. Si el argumento de un método puede ser nulo , entonces, desde una perspectiva de código puro, pasar nulo sigue siendo preferible a pasar Opcional. Además, puede crear métodos sobrecargados para manejar de forma abstracta la ausencia de un argumento de método nulo.
-
Para representar un objeto de Colección que falta. Las colecciones pueden estar vacías, por lo que se debe utilizar una Colección vacía , como un Conjunto o Lista vacío, para representar una Colección sin valores.
GO TO FULL VERSION