java - type - optional map if not present
Estilo funcional de Java 8''s Optional.ifPresent y if-not-Present? (7)
En Java 8, quiero hacer algo con un objeto Optional
si está presente, y hacer otra cosa si no está presente.
if (opt.isPresent()) {
System.out.println("found");
} else {
System.out.println("Not found");
}
Sin embargo, este no es un "estilo funcional".
Optional
tiene un método ifPresent()
, pero no puedo encadenar un método orElse()
.
Por lo tanto, no puedo escribir:
opt.ifPresent( x -> System.out.println("found " + x))
.orElse( System.out.println("NOT FOUND"));
En respuesta a @assylias, no creo que Optional.map()
funcione para el siguiente caso:
opt.map( o -> {
System.out.println("while opt is present...");
o.setProperty(xxx);
dao.update(o);
return null;
}).orElseGet( () -> {
System.out.println("create new obj");
dao.save(new obj);
return null;
});
En este caso, cuando opt
está presente, actualizo su propiedad y la guardo en la base de datos. Cuando no está disponible, creo un nuevo obj
y lo obj
en la base de datos.
Nota en las dos lambdas tengo que devolver null
.
Pero cuando opt
está presente, se ejecutarán ambas lambdas. obj
se actualizará y un nuevo objeto se guardará en la base de datos. Esto se debe al return null
en la primera lambda. Y orElseGet()
continuará ejecutándose.
No hay una gran manera de hacerlo de la caja. Si desea utilizar su sintaxis de limpiador de forma regular, puede crear una clase de utilidad para ayudar:
public class OptionalEx {
private boolean isPresent;
private OptionalEx(boolean isPresent) {
this.isPresent = isPresent;
}
public void orElse(Runnable runner) {
if (!isPresent) {
runner.run();
}
}
public static <T> OptionalEx ifPresent(Optional<T> opt, Consumer<? super T> consumer) {
if (opt.isPresent()) {
consumer.accept(opt.get());
return new OptionalEx(true);
}
return new OptionalEx(false);
}
}
Luego puede usar una importación estática en otro lugar para obtener una sintaxis similar a la que busca:
import static com.example.OptionalEx.ifPresent;
ifPresent(opt, x -> System.out.println("found " + x))
.orElse(() -> System.out.println("NOT FOUND"));
Otra solución podría ser siguiente:
Así es como lo usas:
final Opt<String> opt = Opt.of("I''m a cool text");
opt.ifPresent()
.apply(s -> System.out.printf("Text is: %s/n", s))
.elseApply(() -> System.out.println("no text available"));
O en caso de que en el caso del caso de uso opuesto sea cierto:
final Opt<String> opt = Opt.of("This is the text");
opt.ifNotPresent()
.apply(() -> System.out.println("Not present"))
.elseApply(t -> /*do something here*/);
Estos son los ingredientes:
- Pequeña interfaz de función modificada, solo para el método "elseApply"
- Mejora opcional
- Un poco de curring :-)
La interfaz de función mejorada "cosméticamente".
@FunctionalInterface
public interface Fkt<T, R> extends Function<T, R> {
default R elseApply(final T t) {
return this.apply(t);
}
}
Y la clase de contenedor opcional para la mejora:
public class Opt<T> {
private final Optional<T> optional;
private Opt(final Optional<T> theOptional) {
this.optional = theOptional;
}
public static <T> Opt<T> of(final T value) {
return new Opt<>(Optional.of(value));
}
public static <T> Opt<T> of(final Optional<T> optional) {
return new Opt<>(optional);
}
public static <T> Opt<T> ofNullable(final T value) {
return new Opt<>(Optional.ofNullable(value));
}
public static <T> Opt<T> empty() {
return new Opt<>(Optional.empty());
}
private final BiFunction<Consumer<T>, Runnable, Void> ifPresent = (present, notPresent) -> {
if (this.optional.isPresent()) {
present.accept(this.optional.get());
} else {
notPresent.run();
}
return null;
};
private final BiFunction<Runnable, Consumer<T>, Void> ifNotPresent = (notPresent, present) -> {
if (!this.optional.isPresent()) {
notPresent.run();
} else {
present.accept(this.optional.get());
}
return null;
};
public Fkt<Consumer<T>, Fkt<Runnable, Void>> ifPresent() {
return Opt.curry(this.ifPresent);
}
public Fkt<Runnable, Fkt<Consumer<T>, Void>> ifNotPresent() {
return Opt.curry(this.ifNotPresent);
}
private static <X, Y, Z> Fkt<X, Fkt<Y, Z>> curry(final BiFunction<X, Y, Z> function) {
return (final X x) -> (final Y y) -> function.apply(x, y);
}
}
Esto debería ser el truco y podría servir como plantilla básica para lidiar con dichos requisitos.
La idea básica aquí es seguir. En un mundo de programación de estilo no funcional probablemente implementaría un método tomando dos parámetros donde el primero es un tipo de código ejecutable que debería ejecutarse en caso de que el valor esté disponible y el otro parámetro sea el código ejecutable que debería ejecutarse en caso de que el valor no está disponible. En aras de una mejor legibilidad, puede usar curring para dividir la función de dos parámetros en dos funciones de un parámetro cada una. Esto es lo que básicamente hice aquí.
Sugerencia: Opt también proporciona el otro caso de uso en el que desea ejecutar un fragmento de código en caso de que el valor no esté disponible. Esto podría hacerse también a través de Optional.filter.stuff, pero esto me pareció mucho más legible.
¡Espero que ayude!
Buena programación :-)
Otra solución sería usar funciones de orden superior de la siguiente manera
opt.<Runnable>map(value -> () -> System.out.println("Found " + value))
.orElse(() -> System.out.println("Not Found"))
.run();
Para mí, la respuesta de @Dane White está bien, primero no me gusta usar Runnable pero no pude encontrar ninguna alternativa, aquí otra implementación prefiero más
public class OptionalConsumer<T> {
private Optional<T> optional;
private OptionalConsumer(Optional<T> optional) {
this.optional = optional;
}
public static <T> OptionalConsumer<T> of(Optional<T> optional) {
return new OptionalConsumer<>(optional);
}
public OptionalConsumer<T> ifPresent(Consumer<T> c) {
optional.ifPresent(c);
return this;
}
public OptionalConsumer<T> ifNotPresent(Runnable r) {
if (!optional.isPresent())
r.run();
return this;
}
}
Entonces :
Optional<Any> o = Optional.of(...);
OptionalConsumer.of(o).ifPresent(s ->System.out.println("isPresent "+s))
.ifNotPresent(() -> System.out.println("! isPresent"));
Actualización 1:
la solución anterior para la forma tradicional de desarrollo cuando tiene el valor y desea procesarlo, pero ¿qué sucede si quiero definir la funcionalidad y la ejecución será entonces? Compruebe la mejora a continuación;
public class OptionalConsumer<T> implements Consumer<Optional<T>> {
private final Consumer<T> c;
private final Runnable r;
public OptionalConsumer(Consumer<T> c, Runnable r) {
super();
this.c = c;
this.r = r;
}
public static <T> OptionalConsumer<T> of(Consumer<T> c, Runnable r) {
return new OptionalConsumer(c, r);
}
@Override
public void accept(Optional<T> t) {
if (t.isPresent())
c.accept(t.get());
else
r.run();
}
}
Entonces podría usarse como:
Consumer<Optional<Integer>> c=OptionalConsumer.of(System.out::println, ()->{System.out.println("Not fit");});
IntStream.range(0, 100).boxed().map(i->Optional.of(i).filter(j->j%2==0)).forEach(c);
En este nuevo código tienes 3 cosas:
- puede definir la funcionalidad antes de existir de objeto fácil.
- no crea refrencia de objeto para cada Opcional, solo uno, tiene menos memoria y menos GC.
- está implementando el consumidor para un mejor uso con otros componentes.
por cierto, ahora su nombre es más descriptivo, en realidad es el consumidor>
Si está utilizando Java 9, puede usar el método ifPresentOrElse()
:
opt.ifPresentOrElse(
value -> System.out.println("Found: " + value),
() -> System.out.println("Not found")
);
Una alternativa es:
System.out.println(opt.map(o -> "Found")
.orElse("Not found"));
No creo que mejore la legibilidad sin embargo.
O como sugirió Marko, use un operador ternario:
System.out.println(opt.isPresent() ? "Found" : "Not found");
Ver excelente Opcional en la hoja de trucos de Java 8 .
Proporciona todas las respuestas para la mayoría de los casos de uso.
Breve resumen a continuación
ifPresent () - hacer algo cuando se configura Opcional
opt.ifPresent(x -> print(x));
opt.ifPresent(this::print);
filter () - rechazar (filtrar) ciertos valores opcionales.
opt.filter(x -> x.contains("ab")).ifPresent(this::print);
map () - transforma el valor si está presente
opt.map(String::trim).filter(t -> t.length() > 1).ifPresent(this::print);
orElse () / orElseGet () - volviendo vacío Opcional a T predeterminado
int len = opt.map(String::length).orElse(-1);
int len = opt.
map(String::length).
orElseGet(() -> slowDefault()); //orElseGet(this::slowDefault)
orElseThrow (): lazyly throw exceptions en empty Opcional
opt.
filter(s -> !s.isEmpty()).
map(s -> s.charAt(0)).
orElseThrow(IllegalArgumentException::new);