Ordnungsgemäße Verwendung von Optional.ifPresent ()

94

Ich versuche die ifPresent()Methode der OptionalAPI in Java 8 zu verstehen .

Ich habe eine einfache Logik:

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

Dies führt jedoch zu einem Kompilierungsfehler:

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

Natürlich kann ich so etwas machen:

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

Aber das ist genau wie ein überfüllter nullScheck.

Wenn ich den Code in diesen ändere:

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

Der Code wird schmutziger, was mich daran erinnert, zum alten nullScheck zurückzukehren.

Irgendwelche Ideen?

Rayman
quelle

Antworten:

154

Optional<User>.ifPresent()nimmt ein Consumer<? super User>als Argument. Sie übergeben ihm einen Ausdruck, dessen Typ nichtig ist. Das wird also nicht kompiliert.

Ein Verbraucher soll als Lambda-Ausdruck implementiert werden:

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

Oder noch einfacher mit einer Methodenreferenz:

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

Dies ist im Grunde das gleiche wie

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

Die Idee ist, dass der doSomethingWithUser()Methodenaufruf nur ausgeführt wird, wenn der Benutzer anwesend ist. Ihr Code führt den Methodenaufruf direkt aus und versucht, sein ungültiges Ergebnis an zu übergeben ifPresent().

JB Nizet
quelle
2
Dieser Code wird unübersichtlich. Eine Nullprüfung wird viel sauberer. Denken Sie nicht besonders, dass doSomethingWithUser keine statische Methode ist
Rayman
4
Welcher Code? Diejenige, die Sie verwenden sollten, ist die zweite, die die Instanzmethode (dh nicht statisch) doSomethingWithUser () aufruft. Ich sehe nicht, wie überladen es ist. Der letzte Code soll Ihnen das Äquivalent des Lambda in einer Welt vor dem Lambda erklären. Benutze es nicht.
JB Nizet
2
Ja, aber Sie sind möglicherweise an anonyme Klassen gewöhnt und verstehen daher, was das Lambda tut, indem Sie ein anonymes Klassenäquivalent sehen. Das ist der Punkt.
JB Nizet
1
Sie müssen nichts ändern. Lassen Sie es wie es ist und verwenden Sie das zweite Beispiel:user.ifPresent(this::doSomethingWithUser);
JB Nizet
9
@rayman Wenn Sie eine Funktion haben, die zurückgibt, müssen Sie diese Optional<User>häufig nicht in einer lokalen Variablen speichern. Verketten Sie einfach die Methodenaufrufe:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Stuart Marks
19

Zusätzlich zur Antwort von @ JBNizet besteht mein allgemeiner Anwendungsfall ifPresentdarin, zu kombinieren .isPresent()und .get():

Alter Weg:

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

Neuer Weg:

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

Das ist für mich intuitiver.

cst1992
quelle
8

Warum komplizierten Code schreiben, wenn Sie es einfach machen könnten?

In der Tat, wenn Sie die OptionalKlasse unbedingt verwenden wollen , ist der einfachste Code der , den Sie bereits geschrieben haben ...

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

Dieser Code hat die Vorteile des Seins

  1. lesbar
  2. einfach zu debuggen (Haltepunkt)
  3. nicht schwierig

Nur weil Oracle die OptionalKlasse in Java 8 hinzugefügt hat, bedeutet dies nicht, dass diese Klasse in allen Situationen verwendet werden muss.

schlebe
quelle
1
Der Hauptvorteil der Verwendung von ifPresent besteht darin, dass Sie get () nicht mehr manuell aufrufen müssen. Das manuelle Aufrufen von get () ist fehleranfällig, da man leicht vergisst, zuerst isPresent zu überprüfen, aber man kann es nicht vergessen, wenn man ifPresent verwendet
Dustinroepsch
Ok, und jedes Mal, wenn Sie ein 'Benutzer'-Objekt verwenden, sollten Sie .ifPresent () aufrufen. Der Code wird schnell unlesbar, weil Sie .ifPresent () zu viel Zeit lesen!
Schlebe
2
Um die Rechtschreibfehler auf Ihrer Profilseite ( VB.Net , Netbeans , SqlServer , PostGresql , MySql und Linq) zu beheben , können Sie meinen Dienst nutzen . Es gibt auch eine entsprechende Wortliste .
Peter Mortensen
7

Verwenden Sie flatMap. Wenn ein Wert vorhanden ist, gibt flatMap einen sequentiellen Stream zurück, der nur diesen Wert enthält. Andernfalls wird ein leerer Stream zurückgegeben. Es besteht also keine Notwendigkeit zu verwenden ifPresent(). Beispiel:

list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());
Taras Melnyk
quelle
3
Optional :: Stream benötigt Java9
Avmohan
6

Sie können eine Methodenreferenz wie folgt verwenden:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

Methode ifPresent()get Consumerobject als Paremeter und (von JavaDoc ): "Wenn ein Wert vorhanden ist, rufen Sie den angegebenen Consumer mit dem Wert auf." Wert es ist Ihre Variable user.

Wenn sich diese Methode doSomethingWithUserin der UserKlasse befindet und nicht static, können Sie die Methodenreferenz wie folgt verwenden:

user.ifPresent(this::doSomethingWithUser);
Aleksandr Podkutin
quelle
1
DoSomethingWithUser ist jedoch weder eine statische Methode noch eine Klasse.
Rayman
@rayman Ok, wenn nicht statisch, können Sie dies tun:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Aleksandr Podkutin
6
@AleksandrPodkutin Sie sollten keine neue Instanz der Klasse erstellen, um nur eine Methode auszuführen. Aus dem OP heraus klingt es so, als ob sich die Methode in derselben Klasse befindet, von der aus sie aufgerufen wird. Daher sollte eruser.ifPresent(this::doSomethingWithUser);
Marv
@ Marv Ich sehe keine Bestätigung von OP, dass es in der gleichen Klasse ist. Aber wenn Sie solche Gefühle haben, stimme ich zu, dass er verwenden muss user.ifPresent(this::doSomethingWithUser);. Ich werde es meiner Antwort hinzufügen.
Aleksandr Podkutin