Правильное использование Optional.ifPresent()

Я пытаюсь понять метод ifPresent() API Optional в Java 8.

У меня простая логика:

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

Но это приводит к ошибке компиляции:

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

Конечно, я могу сделать что-то вроде этого:

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

Но это точно как захламленный null чек.

Если я изменю код на это:

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

Код становится все грязнее, что наводит меня на мысль вернуться к старой проверке null.

Любые идеи?


person rayman    schedule 15.06.2014    source источник


Ответы (5)


Optional<User>.ifPresent() принимает Consumer<? super User> в качестве аргумента. Вы передаете ему выражение, тип которого недействителен. Так что не компилируется.

Потребитель предназначен для реализации в виде лямбда-выражения:

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

Или еще проще, используя ссылку на метод:

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

Это в основном то же самое, что и

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

Идея состоит в том, что вызов метода doSomethingWithUser() будет выполняться только в том случае, если пользователь присутствует. Ваш код выполняет вызов метода напрямую и пытается передать результат void в ifPresent().

person JB Nizet    schedule 15.06.2014
comment
Этот код становится загроможденным. Нулевая проверка будет намного чище. вам не кажется, что doSomethingWithUser не является статическим методом? - person rayman; 15.06.2014
comment
Какой код? Вы должны использовать второй, который вызывает метод экземпляра (т.е. нестатический) doSomethingWithUser(). Я не вижу, как это загромождено. Последний код должен объяснить вам эквивалент лямбда в мире до лямбда. Не используйте его. - person JB Nizet; 15.06.2014
comment
Ааа я тебя понимаю. Если я не в лямбда-мире, использование дополнительного API нецелесообразно. - person rayman; 15.06.2014
comment
Да, но вы могли привыкнуть к анонимным классам и, таким образом, понять, что делает лямбда, увидев эквивалент анонимного класса. В этом-то и дело. - person JB Nizet; 15.06.2014
comment
Как бы вы изменили doSomethingWithUser, чтобы насладиться полностью завершенным миром лучших лямбда-выражений? - person rayman; 15.06.2014
comment
Вам нечего модифицировать. Оставьте как есть и используйте второй пример: user.ifPresent(this::doSomethingWithUser); - person JB Nizet; 15.06.2014
comment
@rayman Если у вас есть функция, которая возвращает Optional<User>, часто нет необходимости хранить ее в локальной переменной. Просто свяжите вызовы методов: funcThatMightReturnUser().ifPresent(this::doSomethingWithUser); - person Stuart Marks; 15.06.2014
comment
Я знаю, что это воскрешает старый поток, но стоит отметить, что большое преимущество использования опций заключается в том, что проверки на null больше не нужны, и если вы нарушили контракт, используя Option.of() (вместо Optional.ofNullable() ), эта вставка нуля вызовет исключение NullPointerException (которое быстро терпит неудачу) и не будет бесполезно использовать полиморфизм. - person Blake Neal; 11.07.2018

В дополнение к ответу @JBNizet мой общий вариант использования ifPresent заключается в объединении .isPresent() и .get():

Старый способ:

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

Новый способ:

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

Это, как по мне, более интуитивно понятно.

person cst1992    schedule 26.04.2017
comment
но все, что находится внутри, если присутствует, должно быть возвращено недействительным, потому что все, что вы возвращаете изнутри, теряется - person valik; 15.04.2021
comment
@valik Да, это так. Вы не должны ожидать возврата значения оттуда; это больше похоже на это. - person cst1992; 16.04.2021

Зачем писать сложный код, если можно сделать его простым?

Действительно, если вы абсолютно точно собираетесь использовать класс Optional, самый простой код — это то, что вы уже написали…

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

Этот код имеет то преимущество, что

  1. удобочитаемый
  2. легко отлаживать (точка останова)
  3. не сложно

Тот факт, что Oracle добавил класс Optional в Java 8, не означает, что этот класс нужно использовать во всех ситуациях.

person schlebe    schedule 13.12.2018
comment
Основное преимущество использования ifPresent заключается в том, что вам больше не нужно вызывать get() вручную. Вызов get() вручную подвержен ошибкам, так как легко забыть сначала проверить isPresent, но вы не сможете забыть, если используете ifPresent. - person dustinroepsch; 03.12.2019
comment
Хорошо, и каждый раз, когда вы будете использовать объект «пользователь», вы должны вызывать .ifPresent(). Код быстро станет нечитаемым, потому что вы будете читать .ifPresent() слишком много времени! - person schlebe; 03.12.2019

Вы можете использовать ссылку на метод следующим образом:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

Метод ifPresent() получает объект Consumer в качестве параметра и (из JavaDoc): "Если значение присутствует, вызвать указанного потребителя со значением." Значение это ваша переменная user.

Или, если этот метод doSomethingWithUser находится в классе User, а не в классе static, вы можете использовать ссылку на метод следующим образом:

user.ifPresent(this::doSomethingWithUser);
person Aleksandr Podkutin    schedule 15.06.2014
comment
Но doSomethingWithUser не является ни статическим методом, ни классом. - person rayman; 15.06.2014
comment
@rayman Хорошо, если не статично, вы можете сделать так: user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser); - person Aleksandr Podkutin; 15.06.2014
comment
@AleksandrPodkutin вам не следует создавать новый экземпляр класса только для запуска одного метода, из ОП звучит так, будто метод находится в том же классе, из которого он вызывается, поэтому он должен использовать user.ifPresent(this::doSomethingWithUser); - person Marv; 28.01.2018
comment
@Marv Я не вижу никаких подтверждений формы OP, что он принадлежит к тому же классу. Но если у вас есть такие чувства, я согласен, что он должен использовать user.ifPresent(this::doSomethingWithUser);. Я добавлю это к своему ответу. - person Aleksandr Podkutin; 08.07.2019

Используйте плоскую карту. Если значение присутствует, flatMap возвращает последовательный поток, содержащий только это значение, в противном случае возвращает пустой поток. Поэтому нет необходимости использовать ifPresent() . Пример:

list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());
person Taras Melnyk    schedule 20.03.2018
comment
Необязательно:: для потока требуется java9 - person avmohan; 03.07.2018