Uso corretto di Optional.ifPresent ()


95

Sto cercando di capire il ifPresent()metodo diOptional dell'API in Java 8.

Ho una logica semplice:

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

Ma questo si traduce in un errore di compilazione:

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

Ovviamente posso fare qualcosa del genere:

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

Ma questo è esattamente come un nullassegno disordinato .

Se cambio il codice in questo:

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

Il codice si sta sporcando, il che mi fa pensare di tornare al vecchio nullcontrollo.

Qualche idea?

Risposte:


155

Optional<User>.ifPresent()accetta un Consumer<? super User>argomento. Stai passando un'espressione il cui tipo è nullo. Quindi non viene compilato.

Un consumatore è pensato per essere implementato come espressione lambda:

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

O ancora più semplice, utilizzando un metodo di riferimento:

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

Questa è fondamentalmente la stessa cosa di

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

L'idea è che la doSomethingWithUser()chiamata al metodo verrà eseguita solo se l'utente è presente. Il codice esegue direttamente la chiamata al metodo e cerca di passare il risultato void a ifPresent().


2
Quel codice sta diventando ingombra .. un controllo nullo sarà molto più pulito. non credi che sia proprio il fatto che doSomethingWithUser non è un metodo statico
rayman

4
Quale codice? Quello che dovresti usare è il secondo, che chiama il metodo di istanza (cioè non statico) doSomethingWithUser (). Non vedo come sia ingombra. L'ultimo codice è lì per spiegarti l'equivalente del lambda in un mondo pre-lambda. Non usarlo.
JB Nizet

2
Sì, ma potresti essere abituato a classi anonime e quindi capire cosa fa lambda vedendo un equivalente di classe anonimo. Questo è il punto.
JB Nizet

1
Non hai niente da modificare. Lascialo così com'è e usa il secondo esempio:user.ifPresent(this::doSomethingWithUser);
JB Nizet

10
@rayman Se hai una funzione che restituisce Optional<User>spesso non è necessario memorizzarla in una variabile locale. Basta concatenare le chiamate al metodo:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Stuart Marks

20

Oltre alla risposta di @ JBNizet, il mio caso d'uso generale per ifPresentè combinare .isPresent()e.get() :

Vecchio modo:

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

Nuovo modo:

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

Questo, per me, è più intuitivo.


8

Perché scrivere codice complicato quando potresti renderlo semplice?

In effetti, se intendi assolutamente utilizzare la Optionalclasse, il codice più semplice è quello che hai già scritto ...

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

Questo codice ha i vantaggi di essere

  1. leggibile
  2. facile da eseguire il debug (punto di interruzione)
  3. non complicato

Solo perché Oracle ha aggiunto la Optionalclasse in Java 8 non significa che questa classe debba essere utilizzata in tutte le situazioni.


1
Il vantaggio principale dell'utilizzo di ifPresent è che elimina la necessità di chiamare manualmente get (). Chiamare get () manualmente è soggetto a errori, poiché è facile dimenticare di controllare prima isPresent, ma è impossibile dimenticarlo se usi ifPresent
dustinroepsch

1
Ok e ogni volta che utilizzerai l'oggetto 'utente' dovresti chiamare .ifPresent (). Il codice diventerà rapidamente illeggibile perché leggerai .ifPresent () troppo tempo!
schlebe

2
Per correggere gli errori di ortografia nella pagina del tuo profilo ( VB.Net , Netbeans , SqlServer , PostGresql , MySql e Linq, puoi usare il mio servizio . C'è anche un elenco di parole corrispondente .
Peter Mortensen

7

Usa flatMap. Se è presente un valore, flatMap restituisce un flusso sequenziale contenente solo quel valore, altrimenti restituisce un flusso vuoto. Quindi non è necessario utilizzare ifPresent(). Esempio:

list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());

3
Facoltativo :: stream richiede java9
avmohan

7

Puoi usare il metodo di riferimento in questo modo:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

Il metodo ifPresent()ottiene l' Consumeroggetto come parametro e (da JavaDoc ): "Se è presente un valore, richiama il consumatore specificato con il valore." Valore è la tua variabileuser .

O se questo metodo doSomethingWithUserè nella Userclasse e non lo è static, puoi usare il metodo di riferimento in questo modo:

user.ifPresent(this::doSomethingWithUser);

1
Ma doSomethingWithUser non è un metodo statico né è una classe.
rayman

@rayman Ok, se non statico puoi fare così:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Aleksandr Podkutin

7
@AleksandrPodkutin non dovresti creare una nuova istanza della classe solo per eseguire un metodo, dall'OP sembra che il metodo sia nella stessa classe da cui viene chiamato, quindi dovrebbe usareuser.ifPresent(this::doSomethingWithUser);
Marv

@ Marv Non vedo alcun modulo di affermazione OP che sia nella stessa classe. Ma se hai questi sentimenti, sono d'accordo che deve usare user.ifPresent(this::doSomethingWithUser);. Lo aggiungerò alla mia risposta.
Aleksandr Podkutin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.