Uso adecuado de Optional.ifPresent ()

95

Estoy tratando de comprender el ifPresent()método de laOptional API en Java 8.

Tengo una lógica simple:

Optional<User> user=...
user.ifPresent(doSomethingWithUser(user.get()));

Pero esto resulta en un error de compilación:

ifPresent(java.util.functionError:(186, 74) java: 'void' type not allowed here)

Por supuesto que puedo hacer algo como esto:

if(user.isPresent())
{
  doSomethingWithUser(user.get());
}

Pero esto es exactamente como un desordenado null cheque .

Si cambio el código a esto:

 user.ifPresent(new Consumer<User>() {
            @Override public void accept(User user) {
                doSomethingWithUser(user.get());
            }
        });

El código se está volviendo más sucio, lo que me hace pensar en volver al antiguo null cheque anterior.

¿Algunas ideas?

Rayman
fuente

Respuestas:

155

Optional<User>.ifPresent()toma Consumer<? super User>como argumento. Le está pasando una expresión cuyo tipo es nulo. Entonces eso no se compila.

Se pretende que un consumidor se implemente como una expresión lambda:

Optional<User> user = ...
user.ifPresent(theUser -> doSomethingWithUser(theUser));

O incluso más simple, usando una referencia de método:

Optional<User> user = ...
user.ifPresent(this::doSomethingWithUser);

Esto es básicamente lo mismo que

Optional<User> user = ...
user.ifPresent(new Consumer<User>() {
    @Override
    public void accept(User theUser) {
        doSomethingWithUser(theUser);
    }
});

La idea es que la doSomethingWithUser()llamada al método solo se ejecute si el usuario está presente. Su código ejecuta la llamada al método directamente e intenta pasar su resultado nulo a ifPresent().

JB Nizet
fuente
2
Ese código se está desordenando ... una comprobación nula será mucho más limpia. ¿no crees que s especialmente doSomethingWithUser no es un método estático?
Rayman
4
Cual codigo? El que debe usar es el segundo, que llama al método de instancia (es decir, no estático) doSomethingWithUser (). No veo cómo está desordenado. El último código está ahí para explicarle el equivalente de lambda en un mundo pre-lambda. No lo use.
JB Nizet
2
Sí, pero es posible que esté acostumbrado a clases anónimas y, por lo tanto, comprenda lo que hace la lambda al ver un equivalente de clase anónima. Ese es el punto.
JB Nizet
1
No tienes nada que modificar. Déjelo como está y use el segundo ejemplo:user.ifPresent(this::doSomethingWithUser);
JB Nizet
10
@rayman Si tiene una función que devuelve, a Optional<User>menudo no es necesario almacenarla en una variable local. Simplemente encadena las llamadas al método:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Stuart Marks
20

Además de la respuesta de @ JBNizet, mi caso de uso general ifPresentes combinar .isPresent()y.get() :

Vieja forma:

Optional opt = getIntOptional();
if(opt.isPresent()) {
    Integer value = opt.get();
    // do something with value
}

Nueva manera:

Optional opt = getIntOptional();
opt.ifPresent(value -> {
    // do something with value
})

Esto, para mí, es más intuitivo.

cst1992
fuente
8

¿Por qué escribir código complicado cuando podrías hacerlo simple?

De hecho, si absolutamente va a utilizar la Optionalclase, el código más simple es el que ya ha escrito ...

if (user.isPresent())
{
    doSomethingWithUser(user.get());
}

Este código tiene las ventajas de ser

  1. legible
  2. fácil de depurar (punto de interrupción)
  3. no es complicado

El hecho de que Oracle haya agregado la Optionalclase en Java 8 no significa que esta clase deba usarse en todas las situaciones.

Schlebe
fuente
1
El principal beneficio de usar ifPresent es que elimina la necesidad de llamar a get () manualmente. Llamar a get () manualmente es propenso a errores, ya que es fácil olvidarse de verificar primero isPresent, pero es imposible que lo olvide si usa ifPresent
dustinroepsch
1
Ok y cada vez que use el objeto 'usuario' debe llamar a .ifPresent (). El código se volverá ilegible rápidamente porque leerá .ifPresent () demasiado tiempo.
schlebe
2
Para la fijación de las faltas de ortografía en su página de perfil ( VB.Net , Netbeans , SqlServer , PostgreSQL , MySQL , y LINQ, puede usar mi servicio También existe. Una lista de palabras correspondientes .
Peter Mortensen
7

Utilice flatMap. Si hay un valor presente, flatMap devuelve una secuencia secuencial que contiene solo ese valor; de lo contrario, devuelve una secuencia vacía. Entonces no hay necesidad de usar ifPresent(). Ejemplo:

list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());
Taras Melnyk
fuente
3
Opcional :: stream necesita java9
avmohan
7

Puede usar una referencia de método como esta:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

El método ifPresent()obtiene el Consumerobjeto como parámetro y (de JavaDoc ): "Si hay un valor, invoca al consumidor especificado con el valor". Valora es tu variable user.

O si este método doSomethingWithUserestá en la Userclase y no lo está static, puede usar una referencia de método como esta:

user.ifPresent(this::doSomethingWithUser);
Aleksandr Podkutin
fuente
1
Pero doSomethingWithUser no es un método estático ni es una clase.
Rayman
@rayman Ok, si no estático, puede hacer esto: user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Aleksandr Podkutin
7
@AleksandrPodkutin, no debe crear una nueva instancia de la clase solo para ejecutar un método, desde el OP parece que el método está en la misma clase desde la que se llama, por lo que debe usaruser.ifPresent(this::doSomethingWithUser);
Marv
@Marv No veo ninguna afirmación de OP de que esté en la misma clase. Pero si tienes esos sentimientos, estoy de acuerdo en que tiene que usar user.ifPresent(this::doSomethingWithUser);. Lo agregaré a mi respuesta.
Aleksandr Podkutin