Ejemplos de Uso
Ahora que hemos aprendido por qué son usados los Optional
s, echemos un vistazo a lo que realmente podemos hacer con ellos en Java. Estos ejemplos de código (y Sponge) usan la clase `java.util.Optional<https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html>`_ disponible en Java 8.
Obteniendo el Valor envuelto
El método get()
desplegará un `` Optional`` y devolverá el valor envuelto. Si no hay ningún valor presente, al llamar a get()
arrojará una NoSuchElementException
, por lo que primero se debe realizar una comprobación de presencia.
Optional<String> opt = getOptionalString();
String wrappedString = opt.get();
Manejo de Valores ausentes
El propósito del tipo Optional
es representar un valor que podría o no podría estar allí. Como tal, muchos casos de uso giran en torno al manejo de valores ausentes.
Verificación de presencia
El método isPresent()
devuelve verdadero si hay un valor presente en Optional
. Puede proporcionar la verificación más básica y es equivalente a una comprobación clásica de null
.
Optional<String> opt = getOptionalString();
if (opt.isPresent()) {
String wrappedString = opt.get();
// more code
}
Usar Valores predeterminados
Un patrón común es volver a un valor predeterminado si no hay ninguno presente. El método orElse()
permite una declaración de línea única que devolverá el valor presente en Optional
o el valor predeterminado proporcionado.
En lugar de
Optional<String> optionalString = optionalString();
String someString;
if (optionalString.isPresent()) {
someString = optionalString.get();
} else {
someString = DEFAULT_STRING;
}
solo use
String someString = getOptionalString().orElse(DEFAULT_STRING);
En algunos casos, un valor predeterminado debe calcularse de manera tal que tenga efectos secundarios o si es particularmente costoso. En tal caso, es deseable calcular el valor predeterminado solo si es necesario (evaluación diferida). El método orElseGet()
acepta un Supplier
en lugar de un valor precalculado. Si no hay ningún valor presente en el Optional
en sí, se llamará al Supplier
. Como Supplier
es una interfaz funcional, en su lugar se puede pasar una expresión lambda o una referencia de método.
En lugar de
Optional<String> optionalString = optionalString();
String someString;
if (optionalString.isPresent()) {
someString = optionalString.get();
} else {
someString = myPlugin.defaultString();
}
solo use
String someString = getOptionalString().orElseGet(myPlugin::defaultString);
Falla en Valores ausentes
Si un valor que está ausente debe guiar a una excepción, casi siempre es mejor lanzar una excepción personalizada en lugar de confiar en la NoSuchElementException
predeterminada. Si Ud. llama al método orElseThrow()
con un Supplier
, devolverá el valor envuelto si está presente, o arrojará un Throwable
obtenido del Supplier
si Optional
está vacío. Nuevamente, como Supplier
es una interfaz funcional, en su lugar se pueden usar expresiones lambda o referencias de métodos.
En lugar de
Optional<String> optionalString = optionalString();
if (!optionalString.isPresent()) {
throw new MyException();
}
String someString = optionalString.get();
solo use
String someString = getOptionalString().orElseThrow(MyException::new);
Nota
Si el Throwable
provisto por el proveedor es una excepción marcada, también deberá incluirse en la firma de la función circundante (por ejemplo, public void doStuff() throws MyException
)
Ejecución de Código Condicional
Si no se puede usar ningún valor predeterminado, el código que se basa en un valor presente no puede ser ejecutado. Si bien esto podría tratarse en una condición simple, existen otros métodos convenientes.
Valores de Consumo
Si su lógica para manejar el valor actual ya está encapsulada en un Consumer
o una función de parámetro único, el método ifPresent()
aceptará al consumidor (o una referencia de método). Si hay un valor presente en el Optional
, se pasará al consumidor. Si el Optional
está vacío, nada pasará.
En lugar de
Optional<String> optionalString = getOptionalString();
if (optionalString.isPresent()) {
myPlugin.doSomethingWithString(optionalString.get());
}
solo use
Optional<String> optionalString = getOptionalString();
optionalString.ifPresent(s -> myPlugin.doSomethingWithString(s));
o
getOptionalString().ifPresent(myPlugin::doSomethingWithString);
Filtración
También es posible pasar un Predicate
. Solo se conservarán los valores que este Predicate
devuelva verdadero. Si no hay ningún valor presente o Predicate
devuelve false
, se devolverá un Optional
vacío. Desde que este método devuelve una opción, permite el encadenamiento con otros métodos.
En lugar de
Optional<String> optionalString = getOptionalString();
if (optionalString.isPresent()) {
String someString = optionalString.get();
if (stringTester.isPalindromic(someString)) {
myPlugin.doSomethingWithString(someString);
}
}
solo use
getOptionalString()
.filter(stringTester::isPalindromic)
.ifPresent(myPlugin::doSomethingWithString);
Nota
Ni esta función de filtrado ni las funciones de mapeo que se describen a continuación modifican la instancia en la que son llamadas. Optional
s son siempre inmutables.
Mapeo
Otra operación encadenable es mapear el valor potencial a uno diferente. Si no hay ningún valor presente, nada cambiará. Pero si está presente, el método map()
devolverá un Optional
del valor devuelto por la Function
proporcionada (o un Optional
vacío si ese valor de retorno es null
).
En lugar de
Optional<String> optionalString = getOptionalString();
if (optionalString.isPresent()) {
String someString = optionalString.toLowerCase();
myPlugin.doSomethingWithString(someString);
}
solo use
getOptionalString()
.map(s -> s.toLowerCase())
.ifPresent(myPlugin::doSomethingWithString);
Truco
Si su función de mapeo ya devuelve un Optional
, use el método flatMap()
en su lugar. Este se comportará como map()
, excepto que este espera que la función de mapeo ya devuelva Optional
y por lo tanto no envolverá el resultado.
Ejemplo Combinado
Imagine un plugin que le permita a cada jugador tener un seguimiento de mascota. Asuma la existencia de los siguientes métodos:
petRegistry.getPetForPlayer()
acepta unPlayer
y devuelveOptional<Pet>
. Este método busca la mascota asociada con un jugador dadopetHelper.canSpawn()
aceptandoPet
y devolviendoboolean
. Este método realiza todas las comprobaciones necesarias para asegurarse de que la mascota determinada se genere.petHelper.spawnPet()
acepta unPet
y no devuelve nada. Este método generará una mascota no engendrada previamente.
Ahora desde algún lugar (probablemente la ejecución de un comando) tenemos la variable optionalPlayer
que contiene un Optional<Player>
. Ahora queremos obtener esta mascota de estos jugadores, verificar si la mascota se genera y si no se genera, engendrarla mientras se realizan las verificaciones correspondientes si todos y cada uno de los Optional
realmente contienen un valor. El código que usa solo los métodos básicos isPresent()
y get()
se pone desagradable muy rápido.
if (optionalPlayer.isPresent()) {
Player player = optionalPlayer.get();
Optional<Pet> optionalPet = petRegistry.getPetForPlayer(player);
if (optionalPet.isPresent()) {
Pet pet = optionalPet.get();
if (petHelper.canSpawn(pet)) {
petHelper.spawnPet(pet);
}
}
}
Sin embargo, mediante el uso de los métodos Optional
s para la ejecución de código condicional, todas esas comprobaciones de presencia están ocultas, lo que reduce los niveles repetitivos e indentados y así deja el código mucho mas legible:
optionalPlayer
.flatMap(petRegistry::getPetForPlayer)
.filter(petHelper::canSpawn)
.ifPresent(petHelper::spawnPet);
Creando Opcionales
Si elige proporcionar una API siguiendo el mismo contrato de usar Optional
en lugar de devolver valores null
, Ud. tendrá que crear Optional
s para poder devolverlos. Esto se hace llamando a uno de los tres métodos de construcción estáticos.
Optional.empty()
siempre devolverá un Optional
vacío.
Optional.of()
devolverá un ajuste opcional del valor dado y generará una NullPointerException
si el valor fuera null
.
Optional.ofNullable()
devolverá un Optional
vacío si el valor proporcionado es null
, de lo contrario devolverá un Optional
que envuelve el valor.