Подтвердить что ты не робот

Какой самый элегантный способ комбинировать опционы?

Вот что у меня до сих пор:

Optional<Foo> firstChoice = firstChoice();
Optional<Foo> secondChoice = secondChoice();
return Optional.ofNullable(firstChoice.orElse(secondChoice.orElse(null)));

Это поражает меня как отвратительную, так и расточительную. Если firstChoice присутствует, я ненужно вычисляю secondChoice.

Там также более эффективная версия:

Optional<Foo> firstChoice = firstChoice();
if(firstChoice.isPresent()) {
 return firstChoice;
} else {
 return secondChoice();
}

Здесь я не могу привязать некоторую функцию сопоставления до конца, не дублируя отображение или не объявляя другую локальную переменную. Все это делает код более сложным, чем реальная проблема.

Я бы скорее написал это:

return firstChoice().alternatively(secondChoice());

Однако Необязательный:: альтернативно, очевидно, не существует. Теперь что?

4b9b3361

Ответ 1

Попробуйте следующее:

firstChoice().map(Optional::of)
             .orElseGet(this::secondChoice);

Метод карты дает вам Optional<Optional<Foo>>. Затем метод orElseGet выравнивает это значение до Optional<Foo>. Метод secondChoice будет оцениваться только в том случае, если firstChoice() возвращает пустую опцию.

Ответ 2

Вы можете просто заменить это с помощью

Optional<Foo> firstChoice = firstChoice();
return firstChoice.isPresent()? firstChoice : secondChoice();

Приведенный выше код не будет вызывать, если firstChoice.isPresent() не является ложным.

Но вы должны быть готовы вызвать обе функции, чтобы получить желаемый результат. Нет другого способа избежать проверки.

  • Лучшим случаем является Первый выбор, возвращающий true.
  • В худшем случае будет первый выбор, возвращающий false, следовательно, другой метод выберите второй вариант.

Ответ 3

Возможно, что-то вроде этого:

Optional<String> finalChoice = Optional.ofNullable(firstChoice()
    .orElseGet(() -> secondChoice()
    .orElseGet(() -> null)));

От: Цепочные опции в Java 8

Ответ 4

Здесь обобщение решения @marstran для любого числа опций:

@SafeVarargs
public static <T> Optional<T> selectOptional(Supplier<Optional<T>>... optionals) {
    return Arrays.stream(optionals)
            .reduce((s1, s2) -> () -> s1.get().map(Optional::of).orElseGet(s2))
            .orElse(Optional::empty).get();
}

Тест:

public static Optional<String> first() {
    System.out.println("foo called");
    return Optional.empty();
}

public static Optional<String> second() {
    System.out.println("bar called");
    return Optional.of("bar");
}

public static Optional<String> third() {
    System.out.println("baz called");
    return Optional.of("baz");
}

public static void main(String[] args) {
    System.out.println(selectOptional(() -> first(), () -> second(), () -> third()));
}

Вывод:

foo called
bar called
Optional[bar]

Ответ 5

Я был расстроен из-за того, что это не поддерживалось в java 8, что я переключился на опции guava, у которых or:

public abstract Optional<T> or(Optional<? extends T> secondChoice)

Возвращает этот Необязательный, если у него есть значение; secondChoice в противном случае.

Ответ 6

Ленивые вычисления и произвольное число элементов Optional

Stream.<Supplier<Optional<Foo>>>of(
        this::firstChoice,
        this::secondChoice
).map(
        Supplier::get
).filter(
        Optional::isPresent
).findFirst(
).orElseGet(
    Optional::empty
);

Ответ 7

Вот способ, который работает для произвольного числа Optional, основанного в потоковом API:

return Arrays.asList(firstChoice, secondChoice).stream()
  .filter(Optional::isPresent)
  .map(Optional::get)
  .findFirst().orElse(null);

Это не самый короткий. Но более понятным и понятным.

Другим способом является использование firstNonNull() из Guava of commons-lang, если вы уже используете одну из этих библиотек:

firstNonNull(firstChoice.orElse(null), secondChoice.orElse(null));