Introducción
Contenido
Al escribir código de cualquier tipo en Java, los desarrolladores tienden a trabajar con objetos con más frecuencia que con valores primitivos (int
, boolean
, etc.). Esto sucede porque en el corazón de la programación orientada a objetos hay cosas: permiten a un programador escribir código abstracto de una manera limpia y estructurada.
Además, cada objeto en Java puede tener un valor o no. Si lo hace, su valor se almacena en el montón y la variable que estamos usando tiene una referencia a ese objeto. Si el objeto no tiene valor, este falla null
– un ocupante especial que no muestra ningún valor.
El hecho de que todo puede ser null
, combinado con la tendencia natural a usar cosas en lugar de primitivas, significa que puede (ya veces lo hace) conducir a algo inesperado. NullPointerException
.
Antes de Optional
introdujo una clase en Java 8, como NullPointerException
Los errores eran mucho más comunes en la vida diaria de un programador Java.
En las siguientes secciones, nos centraremos más profundamente en la explicación. Optional
y vea cómo se puede utilizar para superar algunos de los problemas comunes de los valores cero.
La clase electiva
Un Opcional es esencialmente un contenedor. Está diseñado para almacenar un valor o estar «vacío» si el valor no existe; reemplace el null
valor. Como veremos en algunos ejemplos posteriores, este reemplazo es crítico porque permite una verificación nula implícita para todos los objetos que se muestran como Optional
.
Esto significa que ya no es necesario que el programador compruebe explícitamente el valor nulo, sino que lo implementa el propio lenguaje.
Creando Opciones
Veamos lo fácil que es crear escenarios. Optional
y envolviendo cosas que ya tenemos en nuestras aplicaciones.
Usaremos nuestra clase personalizada para esto, el Spaceship
rango:
public class Spaceship {
private Engine engine;
private String pilot;
// Constructor, Getters and Setters
}
Y nuestro Engine
parece que:
public class Engine {
private VelocityMonitor monitor;
// Constructor, Getters and Setters
}
Y ademas, el VelocityMonitor
rango:
public class VelocityMonitor {
private int speed;
// Constructor, Getters and Setters
}
Estas clases son arbitrarias y solo tienen un punto, no hay una implementación real detrás de ellas.
de ()
El primer acercamiento a la creación Optional
utilizando el .of()
método, refiriéndose a un objeto no nulo pasado:
Spaceship falcon = new Spaceship();
Optional<Spaceship> optionalFalcon = Optional.of(falcon);
Si el falcon
fue null
, el método .of()
tuve que NullPointerException
.
Pecado Optional
, intentando acceder a cualquiera de los dominios o métodos falcon
(asumiendo que null
), si no se realiza la verificación nula, el programa se bloqueará.
Le Optional
, la .of()
aviso de método null
valorar y lanzar el NullPointerException
inmediatamente, tal vez también bloquee el programa.
Si el programa falla en ambos enfoques, ¿por qué molestarse siquiera Optional
?
El programa no caería en un lugar más profundo del código (ya que viene falcon
) pero en el primer uso (inicial) null
objeto, minimizando el daño potencial.
ofNullable ()
Ambos falcon
esta permitido null
, en vez de .of()
método, queremos el .ofNullable()
método. Funcionan igual si el valor no esnull
. La diferencia es obvia cuando la referencia se centra en ella. null
en ese caso – el .ofNullable()
el método del desprecio es perfecto con este código:
Spaceship falcon = null;
Optional<Spaceship> optionalFalcon = Optional.ofNullable(falcon);
vacío ()
Y finalmente, en lugar de plegar una variable de referencia existente (null
o nonull
), podemos null
valor en contexto Optional
. Es como un recipiente vacío que devuelve una muestra vacía. Optional
:
Optional<Spaceship> emptyFalcon = Optional.empty();
Verificar valores
Después de la creación Optional
sy empaquetar información en ellos, es natural que queramos acceder a ellos.
Sin embargo, antes de acceder a él, debemos comprobar si hay algún valor, o si hay Optional
está vacío.
está presente ()
Dado que la captura de excepciones es una operación exigente, es mejor utilizar uno de los métodos de la API para comprobar si el valor existe antes de intentar acceder a él, y cambiar el flujo si no.
Si es así, entonces .get()
se puede utilizar un método para acceder al valor. Aunque, más sobre ese método en las partes finales.
Para comprobar si el valor está presente en el interior Optional
, usamos el .isPresent()
método. Esto es básicamente un reemplazo para el null
-comprueba los viejos tiempos:
// Without Optional
Spaceship falcon = hangar.getFalcon();
if (falcon != null) {
System.out.println(falcon.get());
} else {
System.out.printn("The Millennium Falcon is out and about!");
}
// With Optional
Optional<Spaceship> optionalFalcon = Optional.ofNullable(hangar.getFalcon());
if (optionalFalcon.isPresent()) {
System.out.println(falcon.get());
} else {
System.out.println("The Millennium Falcon is out and about!");
}
Desde el falcon
No puedo estar en el hangar también, podemos esperar null
valor, por lo tanto .ofNullable()
se utiliza.
si está presente ()
Haciendo las cosas aún más fáciles Optional
también hay un método condicional que evita completamente la verificación de presencia:
Optional<Spaceship> optionalFalcon = Optional.ofNullable(hangar.getFalcon());
optionalFalcon.ifPresent(System.out::println);
Si hay un valor, el contenido se imprime mediante Referencia de método. Si no hay valor en el contenedor, no pasa nada. Es posible que aún desee utilizar el enfoque anterior si desea una definición else {}
declaración, sin embargo.
Esto ilustra lo que mencionamos anteriormente cuando dijimos que null
-comprueba con Optional
comprendido y aplicado por el sistema de tipos.
esta vacio ()
Otra forma de verificar el valor es usar .isEmpty()
. Básicamente, llamando Optional.isEmpty()
lo mismo que llamar !Optional.isPresent()
. No hay ninguna diferencia particular:
Optional<Spaceship> optionalFalcon = Optional.ofNullable(hangar.getFalcon());
if (optionalFalcon.isEmpty()) {
System.out.println("Please check if the Millennium Falcon has returned in 5 minutes.");
} else {
optionalFalcon.doSomething();
}
Nesting Nesting Checks
Nuestra Spaceship
la clase tiene una característica, como se definió anteriormente Engine
, que tiene una característica VelocityMonitor
.
Suponiendo ahora que queremos acceder al objeto del monitor de velocidad y obtener la velocidad actual de la nave espacial, teniendo en cuenta que todos estos valores pueden ser null
.
Encontrar la velocidad puede verse así:
if (falcon != null) {
Engine engine = falcon.getEngine();
if (engine != null) {
VelocityMonitor monitor = engine.getVelocityMonitor();
if (monitor != null) {
Velocity velocity = monitor.getVelocity();
System.out.println(velocity);
}
}
}
El ejemplo anterior muestra lo ordenado que es realizar tales comprobaciones, sin mencionar la cantidad de código delictivo necesario para que las comprobaciones sean viables en primer lugar.
Una solución alternativa usando Optional
haría:
Velocity velocity = falcon
.flatMap(Spaceship::getEngine)
.flatMap(Engine::getVelocityMonitor)
.map(VelocityMonitor::getVelocity);
Nota: ¿No estás seguro de lo que está pasando arriba? Consulte la explicación a continuación para obtener más detalles.
Con este tipo de enfoque, no se requieren verificaciones explícitas. Si está vacío en alguno de los objetos Optional
, el resultado final también estará vacío Optional
.
Para que este tipo de cosas funcionen, necesitamos nuestras definiciones de Spaceship
y Engine
clases:
public class Spaceship {
private Optional<Engine> engine;
private String pilot;
// Constructor, Getters and Setters
}
public class Engine {
private Optional<VelocityMonitor> monitor;
// Constructor, Getters and Setters
}
Las definiciones de atributos que hemos cambiado son: ahora están dobladas Optional
cosas para hacer posible una solución alternativa de este tipo.
Esto puede resultar un poco tedioso al principio, pero si se planifica desde el principio, se necesita casi la misma cantidad de esfuerzo para escribirlo.
Además, después Optional
un atributo que no sea un objeto regular indica que el atributo puede existir o no. Tenga en cuenta que esto es bastante útil, ya que no tenemos esos significados semánticos con definiciones de atributos regulares.
Explicación de muestra
En esta sección, nos tomaremos un tiempo para explicar el ejemplo anterior con flatMaps
y maps
. Si lo entiende sin más explicaciones, no dude en omitir esta sección.
Se realiza la primera llamada a métodos falcon
que es una especie de Optional<Spaceship>
. Llamando al getEngine
el método devuelve una especie de objeto Optional<Engine>
. La combinación de estos dos tipos hace que el tipo de objeto devuelto Optional<Optional<Engine>>
.
Dado que queremos ver este objeto como Engine
contenedor y hacer más llamadas, necesitamos algún tipo de mecanismo para «quitar la piel exterior» Optional
capa.
Existe tal mecanismo y se le da flatMap
. Este método API combina map
y el flat
operaciones aplicando primero una función a cada uno de los elementos y luego difundiendo el resultado en una secuencia de un solo nivel.
El es map
por otro lado, una función solo se aplica sin difundir la corriente. En nuestro caso, utilice map
y flatMap
que nos daría Optional<Optional<Engine>>
y Optional<Engine>
respectivamente.
Vocación flatMap
en una especie de objeto Optional
por lo que llevaría a un nivel Optional
, lo que nos permite utilizar varias llamadas de métodos similares en sucesión.
Que nos deja para el finalOptional<Engine>
, que queríamos en primer lugar.
Resultados alternativos
.si no ()
El ejemplo anterior se puede ampliar aún más utilizando el orElse(T other)
método. El método devolverá el Optional
un objeto conocido sólo si tiene un valor interno.
Si el Optional
vacío, el método devuelve el other
valor. Básicamente Optional
versión del operador ternario:
// Ternary Operator
Spaceship falcon = maybeFalcon != null ? maybeFalcon : new Spaceship("Millennium Falcon");
// Optional and orElse()
Spaceship falcon = maybeFalcon.orElse(new Spaceship("Millennium Falcon"));
Junto con la ifPresent()
Método, este tipo de enfoque utiliza las expresiones lambda para hacer que el código sea más legible y menos propenso a errores.
.orElseGet ()
En vez de other
valor solo como un argumento, podemos
Proveedor en lugar. La diferencia entre .orElse()
y .orElseGet()
, aunque esto puede no ser obvio a primera vista:
// orElse()
Spaceship falcon = maybeFalcon.orElse(new Spaceship("Millennium Falcon"));
// orElseGet()
Spaceship falcon = maybeFalcon.orElseGet(() -> new Spaceship("Millennium Falcon"));
Ambos maybeFalcon
no hay valor, ambos métodos devolverán un nuevo método Spaceship
. En este caso, su comportamiento es el mismo. La diferencia es obvia si maybeFalcon
hay valor.
En el primer caso, el new Spaceship
un objeto no se devolverá, pero se creará. Esto sucederá independientemente de si el valor existe o no. En el segundo caso, el new Spaceship
creado solo si maybeFalcon
no hay ningún valor.
Es como do-while
realiza la tarea independientemente de la while
bucle, al menos una vez.
Esto puede parecer una diferencia insignificante, pero es muy importante si la creación de una nave espacial es una operación exigente. En primer lugar, siempre estamos creando un nuevo objeto, incluso si nunca se utilizará.
.orElseGet()
debería ser elegido en su lugar .orElse()
en esos casos.
.oElseThrow ()
En lugar de devolver un valor alternativo (como hemos visto en las dos secciones anteriores), podemos lanzar una excepción. Esto se logra con el .orElseThrow()
un método que acepta un proveedor en lugar de un valor alternativo que devuelve la excepción por temor a tener que gastarlo.
Esto puede resultar útil en situaciones en las que el resultado final es muy importante y no debe estar vacío. Quizás la opción más segura es usar una excepción en este caso:
// Throwing an exception
Spaceship falcon = maybeFalcon.orElseThrow(NoFuelException::new);
Obtener valores de opcional
.obtener ()
Después de ver muchas formas diferentes de verificar y acceder al valor interno Optional
, veamos ahora una forma final de encontrar el valor utilizado por algunos de los métodos mostrados anteriormente.
La forma más sencilla de acceder al valor interior Optional
si .get()
. Este método devuelve el valor actual o arroja un NoSuchElementException
si el valor está ausente:
Optional<Spaceship> optionalFalcon = Optional.ofNullable(hangar.getFalcon());
if (falcon.isPresent()) {
Spaceship falcon = optionalFalcon.get()
// Fly the falcon
}
Como era de esperar, el .get()
modo de no retornonull
por ejemplo Spaceship
clase y asigna a la falcon
objeto.
Conclusión
Optional
Java se introdujo como una forma de resolver los problemas con null
referencias. antes de Optional
, todos los objetos podían tener o no tener un valor (es decir, tener null
).
Introducción Optional
básicamente hace cumplir null
– Verificar por el sistema de tipos de modo que tales verificaciones no tengan que realizarse manualmente.
Este fue un paso importante para mejorar el lenguaje y su usabilidad al agregar un conjunto adicional de verificaciones de tipo. Usando este sistema en lugar del antiguo null
La verificación permite escribir un código claro y conciso sin la necesidad de agregar una placa de caldera y realizar comprobaciones agotadoras manualmente.
.