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

99

Я пытаюсь понять ifPresent() метод OptionalAPI в 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 чеку.

Любые идеи?

Rayman
источник

Ответы:

160

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()вызов метода будет выполняться только при наличии пользователя. Ваш код выполняет вызов метода напрямую и пытается передать свой недействительный результат в ifPresent().

JB Nizet
источник
2
Этот код становится загроможденным ... нулевая проверка будет намного чище. Вам не кажется, что doSomethingWithUser не является статическим методом?
Rayman
4
Какой код? Вам следует использовать второй, который вызывает экземплярный (т.е. нестатический) метод doSomethingWithUser (). Я не понимаю, насколько там захламлено. Последний код объясняет вам эквивалент лямбда в мире до лямбда. Не используйте это.
JB Nizet
2
Да, но вы, возможно, привыкли к анонимным классам и, таким образом, понимаете, что делает лямбда, видя эквивалент анонимного класса. В этом-то и дело.
JB Nizet
1
Вам нечего изменять. Оставьте все как есть и используйте второй пример:user.ifPresent(this::doSomethingWithUser);
JB Nizet
11
@rayman Если у вас есть функция, которая возвращает, Optional<User>часто нет необходимости хранить ее в локальной переменной. Просто объедините вызовы методов в цепочку:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Стюарт Маркс
21

В дополнение к ответу @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
})

Для меня это более интуитивно понятно.

cst1992
источник
9

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

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

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

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

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

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

Schlebe
источник
1
Основное преимущество использования ifPresent заключается в том, что он избавляет вас от необходимости когда-либо вызывать get () вручную. Вызов get () вручную подвержен ошибкам, так как легко забыть сначала проверить isPresent, но вы не сможете забыть, если используете ifPresent
dustinroepsch
1
Хорошо, и каждый раз, когда вы будете использовать объект «пользователь», вы должны вызывать .ifPresent (). Код быстро станет нечитабельным, потому что вы будете читать .ifPresent () слишком много времени!
schlebe 02
2
Для исправления орфографических ошибок на странице вашего профиля ( VB.Net , Netbeans , SqlServer , PostGresql , MySql и Linq вы можете использовать мой сервис . Также есть соответствующий список слов .
Питер Мортенсен,
7

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

list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());
Тарас Мельник
источник
3
Необязательно :: потоку нужен java9
avmohan
7

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

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

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

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

user.ifPresent(this::doSomethingWithUser);
Александр Подкутин
источник
1
Но doSomethingWithUser - это не статический метод и не класс.
Rayman
@rayman Ладно, если не статично, можно так:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Александр Подкутин
7
@AleksandrPodkutin, вы не должны создавать новый экземпляр класса только для запуска одного метода, из OP это звучит так, как будто метод находится в том же классе, из которого он вызывается, поэтому он должен использоватьuser.ifPresent(this::doSomethingWithUser);
Marv
@Marv Я не вижу никакой формы подтверждения OP, что он принадлежит к тому же классу. Но если есть такие ощущения, согласен, что им надо пользоваться user.ifPresent(this::doSomethingWithUser);. Я добавлю это к своему ответу.
Александр Подкутин