Prawidłowe użycie Optional.ifPresent ()

99

Próbuję zrozumieć ifPresent()metodę OptionalAPI w Javie 8.

Mam prostą logikę:

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

Ale to powoduje błąd kompilacji:

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

Oczywiście mogę zrobić coś takiego:

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

Ale to jest dokładnie jak zagracony nullczek.

Jeśli zmienię kod na ten:

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

Kod staje się brudniejszy, co sprawia, że ​​myślę o powrocie do starego nullczeku.

Jakieś pomysły?

Rayman
źródło

Odpowiedzi:

160

Optional<User>.ifPresent()przyjmuje Consumer<? super User>jako argument. Przekazujesz mu wyrażenie, którego typ jest nieważny. Więc to się nie kompiluje.

Konsument ma zostać zaimplementowany jako wyrażenie lambda:

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

Lub jeszcze prościej, używając odwołania do metody:

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

To w zasadzie to samo, co

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

Chodzi o to, że doSomethingWithUser()wywołanie metody zostanie wykonane tylko wtedy, gdy użytkownik jest obecny. Twój kod wykonuje wywołanie metody bezpośrednio i próbuje przekazać wynik void do ifPresent().

JB Nizet
źródło
2
Ten kod jest zagracony ... sprawdzanie zerowe będzie znacznie czystsze. nie sądzisz, że specjalnie doSomethingWithUser nie jest metodą statyczną
rayman
4
Który kod? Należy użyć drugiej, która wywołuje instancję (tj. Niestatyczną) metodę doSomethingWithUser (). Nie rozumiem, jak jest zagracony. Ostatni kod ma na celu wyjaśnienie odpowiednika lambdy w świecie sprzed lambdy. Nie używaj tego.
JB Nizet
2
Tak, ale możesz przyzwyczaić się do klas anonimowych i zrozumieć, co robi lambda, widząc anonimowy odpowiednik klasy. O to chodzi.
JB Nizet
1
Nie masz nic do modyfikacji. Zostaw to tak, jak jest i użyj drugiego przykładu:user.ifPresent(this::doSomethingWithUser);
JB Nizet
11
@rayman Jeśli masz funkcję, która zwraca, Optional<User>często nie ma potrzeby przechowywania jej w zmiennej lokalnej. Po prostu połącz wywołania metody:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Stuart Marks
21

Oprócz odpowiedzi @ JBNizet, moim ogólnym przypadkiem użycia ifPresentjest połączenie .isPresent()i .get():

Stara droga:

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

Nowy sposób:

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

Dla mnie jest to bardziej intuicyjne.

cst1992
źródło
9

Po co pisać skomplikowany kod, skoro można to uprościć?

Rzeczywiście, jeśli absolutnie zamierzasz używać tej Optionalklasy, najprostszym kodem jest to, co już napisałeś ...

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

Ten kod ma zalety bycia

  1. czytelny
  2. łatwe do debugowania (punkt przerwania)
  3. nie trudne

To, że Oracle dodała Optionalklasę w Javie 8, nie oznacza, że ​​ta klasa musi być używana w każdej sytuacji.

schlebe
źródło
1
Główną zaletą korzystania z ifPresent jest to, że eliminuje potrzebę ręcznego wywoływania get (). Ręczne wywoływanie get () jest podatne na błędy, ponieważ łatwo zapomnieć o sprawdzeniu najpierw isPresent, ale nie można zapomnieć, jeśli używasz ifPresent
dustinroepsch
1
Ok i za każdym razem, gdy użyjesz obiektu 'user', powinieneś wywołać .ifPresent (). Kod szybko stanie się nieczytelny, ponieważ będziesz czytać .ifPresent () zbyt długo!
schlebe
2
Aby naprawić błędy ortograficzne na stronie swojego profilu ( VB.Net , Netbeans , SqlServer , PostGresql , MySql i Linq, możesz skorzystać z mojej usługi . Istnieje również odpowiednia lista słów .
Peter Mortensen
7

Użyj flatMap. Jeśli wartość jest obecna, flatMap zwraca sekwencyjny Stream zawierający tylko tę wartość, w przeciwnym razie zwraca pusty Stream. Więc nie ma potrzeby używania ifPresent(). Przykład:

list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());
Taras Melnyk
źródło
3
Opcjonalnie :: stream wymaga java9
avmohan
7

Możesz użyć odwołania do metody w następujący sposób:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

Metoda ifPresent()get Consumerobject jako parometr i (z JavaDoc ): "Jeśli wartość jest obecna, wywołaj określonego konsumenta z wartością." Wartość to twoja zmienna user.

Lub jeśli ta metoda doSomethingWithUserznajduje się w Userklasie, a tak nie jest static, możesz użyć odwołania do metody w następujący sposób:

user.ifPresent(this::doSomethingWithUser);
Aleksandr Podkutin
źródło
1
Ale doSomethingWithUser nie jest metodą statyczną ani klasą.
rayman
@rayman Ok, jeśli nie statyczny, możesz zrobić tak:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Aleksandr Podkutin
7
@AleksandrPodkutin nie powinieneś tworzyć nowej instancji klasy tylko po to, aby uruchomić jedną metodę, z OP wygląda na to, że metoda jest w tej samej klasie, z której jest wywoływana, dlatego powinien użyćuser.ifPresent(this::doSomethingWithUser);
Marv
@Marv Nie widzę żadnego formularza potwierdzenia OP, że jest w tej samej klasie. Ale jeśli masz takie uczucia, zgadzam się, że musi skorzystać user.ifPresent(this::doSomethingWithUser);. Dodam to do mojej odpowiedzi.
Aleksandr Podkutin