Я работал с новым необязательным типом в Java 8, и я столкнулся с тем, что похоже на общую операцию, которая не является 't поддерживается функционально: "orElseOptional"
Рассмотрим следующий шаблон:
Optional<Result> resultFromServiceA = serviceA(args);
if (resultFromServiceA.isPresent) return result;
else {
Optional<Result> resultFromServiceB = serviceB(args);
if (resultFromServiceB.isPresent) return resultFromServiceB;
else return serviceC(args);
}
Существует много форм этого шаблона, но он сводится к желанию "orElse" на необязательном, который принимает функцию, производящую новую опцию, называемую только в том случае, если текущий не существует.
Реализация будет выглядеть так:
public Optional<T> orElse(Supplier<Optional<? extends T>> otherSupplier) {
return value != null ? this : other.get();
}
Мне любопытно, есть ли причина, по которой такой метод не существует, если я просто использую Необязательный непреднамеренно, и каким другим способом люди придумали дело с этим случаем.
Я должен сказать, что я думаю, что решения с использованием пользовательских классов/методов не являются элегантными, потому что люди, работающие с моим кодом, не обязательно знают, что они существуют.
Кроме того, если кто-нибудь знает, будет ли такой метод включен в JDK 9, и где я могу предложить такой метод? Это похоже на довольно вопиющее упущение API для меня.
Ответ 1
Это часть JDK 9 в форме or
, которая берет Supplier<Optional<T>>
. Тогда ваш пример:
return serviceA(args)
.or(() -> serviceB(args))
.or(() -> serviceC(args));
Подробнее см. Javadoc или этот пост, который я написал.
Ответ 2
Самый чистый подход "попробуйте услуги" с учетом текущего API:
Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
()->serviceA(args),
()->serviceB(args),
()->serviceC(args),
()->serviceD(args))
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
Важным аспектом является не постоянная цепочка операций, которую вы должны написать один раз, но как легко добавить другую услугу (или изменить список служб в целом). Здесь достаточно добавить или удалить один ()->serviceX(args)
.
Из-за ленивой оценки потоков никакой сервис не будет вызываться, если предыдущая служба вернула непустую Optional
.
Ответ 3
Это не очень, но это сработает:
return serviceA(args)
.map(Optional::of).orElseGet(() -> serviceB(args))
.map(Optional::of).orElseGet(() -> serviceC(args))
.map(Optional::of).orElseGet(() -> serviceD(args));
.map(func).orElseGet(sup)
- довольно удобный шаблон для использования с Optional
. Это означает: "Если этот Optional
содержит значение v
, дайте мне func(v)
, иначе дайте мне sup.get()
".
В этом случае мы называем serviceA(args)
и получаем a Optional<Result>
. Если Optional
содержит значение v
, мы хотим получить Optional.of(v)
, но если он пуст, мы хотим получить serviceB(args)
. Прополощите повтор с другими вариантами.
Другим использованием этого шаблона являются
-
.map(Stream::of).orElseGet(Stream::empty)
-
.map(Collections::singleton).orElseGet(Collections::emptySet)
Ответ 4
Возможно, это то, что вам нужно: получить значение от одного необязательного или другого
В противном случае вам может потребоваться просмотреть Optional.orElseGet
. Вот пример того, что я думаю, что вы после:
result = Optional.ofNullable(serviceA().orElseGet(
() -> serviceB().orElseGet(
() -> serviceC().orElse(null))));
Ответ 5
Это похоже на хорошую совместимость для сопоставления шаблонов и более традиционный интерфейс Option с реализацией Some и None (например, в Javaslang, FunctionalJava) или ленивый Возможно реализация в cyclops-react. Я автор этой библиотеки.
С cyclops-react вы также можете использовать структурное соответствие шаблону по типам JDK. Для Необязательного вы можете сопоставлять существующие и отсутствующие случаи с помощью шаблона . он будет выглядеть примерно так:
import static com.aol.cyclops.Matchables.optional;
optional(serviceA(args)).visit(some -> some ,
() -> optional(serviceB(args)).visit(some -> some,
() -> serviceC(args)));
Ответ 6
Предполагая, что вы все еще на JDK8, есть несколько вариантов.
Вариант №1: сделайте свой собственный вспомогательный метод
Например:
public class Optionals {
static <T> Optional<T> or(Supplier<Optional<T>>... optionals) {
return Arrays.stream(optionals)
.map(Supplier::get)
.filter(Optional::isPresent)
.findFirst()
.orElseGet(Optional::empty);
}
}
Чтобы вы могли это сделать:
return Optionals.or(
()-> serviceA(args),
()-> serviceB(args),
()-> serviceC(args),
()-> serviceD(args)
);
Вариант №2: используйте библиотеку
Например, google guava Optional поддерживает правильную or()
операцию (как JDK9), например:
return serviceA(args)
.or(() -> serviceB(args))
.or(() -> serviceC(args))
.or(() -> serviceD(args));
(Где каждая из служб возвращает com.google.common.base.Optional
, а не java.util.Optional
).