Какой самый элегантный способ комбинировать опционы?
Вот что у меня до сих пор:
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());
Однако Необязательный:: альтернативно, очевидно, не существует. Теперь что?
Ответы
Ответ 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));