Правильне використання Optional.ifPresent ()


94

Я намагаюся зрозуміти 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чеки.

Будь-які ідеї?

Відповіді:


154

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().


2
Цей код стає захаращеним .. нульова перевірка буде набагато чистішою. ти не думаєш спеціально, що doSomethingWithUser не є статичним методом
rayman

4
Який код? Ви повинні використовувати другий, який викликає екземпляр (тобто нестатичний) метод doSomethingWithUser (). Не бачу, як це захаращено. Останній код існує, щоб пояснити вам еквівалент лямбди в доламбда-світі. Не використовуйте його.
JB Nizet

2
Так, але ви можете звикнути до анонімних класів і, отже, зрозуміти, що робить лямбда, побачивши еквівалент анонімного класу. В тім-то й річ.
JB Nizet

1
Вам нічого змінювати. Залиште це як є, і скористайтеся другим прикладом:user.ifPresent(this::doSomethingWithUser);
JB Nizet

10
@rayman Якщо у вас є функція, яка повертається Optional<User>, часто немає необхідності зберігати її в локальній змінній. Просто приєднайте виклики методу:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Стюарт Маркс

19

На додаток до відповіді @ 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
})

Для мене це більш інтуїтивно.


7

Використовуйте flatMap. Якщо значення присутнє, flatMap повертає послідовний потік, що містить лише це значення, інакше повертає порожній потік. Тому немає необхідності у використанні ifPresent(). Приклад:

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

3
Необов’язково :: потік потребує java9
avmohan

7

Навіщо писати складний код, коли ви можете це зробити просто?

Дійсно, якщо ви абсолютно збираєтеся використовувати Optionalклас, найпростіший код - це те, що ви вже написали ...

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

Цей код має переваги існування

  1. читабельний
  2. легко налагоджувати (точка зупинки)
  3. не хитро

Те, що Oracle додав Optionalклас у Java 8, не означає, що цей клас повинен використовуватися в усіх ситуаціях.


1
Основною перевагою використання ifPresent є те, що це позбавляє вас від необхідності коли-небудь викликати get () вручну. Виклик get () вручну схильний до помилок, оскільки легко забути перевірити спочатку isPresent, але неможливо забути, якщо ви використовуєте ifPresent
dustinroepsch

1
Гаразд, і кожного разу, коли ви будете використовувати об'єкт 'user', вам слід зателефонувати .ifPresent (). Код швидко стане нечитабельним, оскільки ви прочитаєте .ifPresent () занадто багато часу!
schlebe

2
Для виправлення орфографічних помилок на вашій сторінці профілю ( VB.Net , Netbeans , SqlServer , PostGresql , MySql та Linq, ви можете скористатися моєю службою . Є також відповідний список слів .
Пітер Мортенсен,

7

Ви можете використовувати посилання на метод так:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

Метод ifPresent()get Consumerobject як параметр і (з JavaDoc ): "Якщо значення є, викличте вказаного споживача зі значенням." Значення це ваша змінна user.

Або якщо цей метод doSomethingWithUserє в Userкласі, а його немає static, ви можете використати посилання на метод таким чином:

user.ifPresent(this::doSomethingWithUser);

1
Але doSomethingWithUser не є статичним методом і не є класом.
rayman

@rayman Добре, якщо не статично, ти можеш зробити так:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Олександр Подкутін

7
@AleksandrPodkutin, вам не слід створювати новий екземпляр класу просто для запуску одного методу; з ОП здається, що метод знаходиться в тому ж класі, з якого його викликають, тому він повинен використовуватиuser.ifPresent(this::doSomethingWithUser);
Marv

@Marv Я не бачу жодної заяви про підтвердження того, що вона в тому ж класі. Але якщо у вас є такі почуття, я погоджуюсь, що він повинен використовувати user.ifPresent(this::doSomethingWithUser);. Я додаю це до своєї відповіді.
Олександр Подкутін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.