Получить значение из одного Необязательного или другого - программирование
Подтвердить что ты не робот

Получить значение из одного Необязательного или другого

У меня есть два экземпляра java.util.Optional, и я хочу получить Optional, который:

  • Имеет значение first Optional, если оно имеет значение.
  • Значение второго необязательно, если оно имеет значение.
  • Пусто от ни одного параметра не имеет значения.

Есть ли прямой способ сделать это, т.е. уже есть какой-нибудь API для этого?

Следующие выражения сделают это, но я должен упомянуть первый необязательный дважды:

firstOptional.isPresent() ? firstOptional : secondOptional

Это именно то, что делает com.google.common.base.Optional.or(), но этот метод отсутствует в Java 8 API.


В принятом ответе aioobe перечислены несколько альтернативных подходов для преодоления этого упущения API-интерфейса Optional, где такое значение должно быть вычислено (что отвечает на мой вопрос). Теперь я решил добавить служебную функцию в свою кодовую базу:

public static <T> Optional<T> or(Optional<T> a, Optional<T> b) {
    if (a.isPresent())
        return a;
    else
        return b;
}
4b9b3361

Ответ 1

Обновление: Начиная с Java 9 (changeet 12885) вы можете сделать

firstOptional.or(() -> secondOptional);

Для Java 8, читайте дальше...

Если вы хотите не упоминать firstOptional дважды, вам, вероятно, придется пойти с чем-то вроде

firstOptional.map(Optional::of).orElse(secondOptional);

или

Optional.ofNullable(firstOptional.orElse(secondOptional.orElse(null)));

Но наиболее читаемым вариантом, вероятно, просто сделать

Optional<...> opt = firstOptional.isPresent()  ? firstOptional
                  : secondOptional.isPresent() ? secondOptional
                  : Optional.empty();

Если кто-то споткнется по этому вопросу, но имеет список опций, я бы предложил что-то вроде

Optional<...> opt = optionals.stream()
                             .filter(Optional::isPresent)
                             .findFirst()
                             .orElse(Optional.empty());

Ответ 2

EDIT: Я полностью думал, что вы использовали Guava Optional изначально. Я обновил свой ответ, чтобы предоставить синтаксис Guava и Java 8 для соответствующих классов Optional.

Java 8 Дополнительно

Вы можете сократить его до:

firstOptional.orElse(secondOptional.orElse(EMPTY_VALUE))

Я не уверен, что вы имели в виду в своей третьей пуле, "пустой". Если вы имели значение null, то это будет трюк:

firstOptional.orElse(secondOptional.orElse(null))

orElse() - это метод на Optional, который вернет значение, если оно присутствует, иначе оно вернет значение, которое вы предоставили в качестве аргумента orElse().

Guava Необязательный

Вы можете сократить его до:

firstOptional.or(secondOptional.or(EMPTY_VALUE))

Я не уверен, что вы имели в виду в своей третьей пуле, "пустой". Если вы имели значение null, то это будет трюк:

firstOptional.or(secondOptional.orNull())

or() - это метод на Optional, который вернет значение, если оно присутствует, иначе оно вернет значение, которое вы предоставили в качестве аргумента or().

Ответ 3

У меня было несколько встреч с проблемой, которая могла быть решена с помощью JDK 9 Optional::or и не могла, потому что мы используем JDK 8. Наконец, я добавил класс util с помощью этого метода:

public static <T> Optional<T> firstPresent(final Supplier<Optional<T>>... optionals) {
    return Stream.of(optionals)
            .map(Supplier::get)
            .filter(Optional::isPresent)
            .findFirst()
            .orElse(Optional.empty());

Теперь вы можете предоставить любое количество опций этому методу, и они будут лениво оценены так:

    final Optional<String> username = OptionalUtil.firstPresent(
            () -> findNameInUserData(user.getBasicData()),
            () -> findNameInUserAddress(user.getAddress()),
            () -> findDefaultUsername());

Теперь findNameInUserAddress вызывается только в том случае, если findNameInUserData возвращает пустое. findDefaultUsername будет вызываться только в том случае, если оба findNameInUserData и findNameInUserAddress возвращаются пустым и т.д.

Ответ 4

Я знаю, что этот вопрос устарел, но почему бы просто не использовать в подобных случаях что-то вроде шаблона цепочки ответственности? Он изготовлен/предназначен для таких случаев, как ваш. И в качестве дополнительного преимущества довольно легко добавить дополнительные сервисы в список.

Вот ссылка, которая описывает шаблон: https://sourcemaking.com/design_patterns/chain_of_responsibility