Необязательно orElse необязательно в Java
Я работаю с новым необязательный тип в Java 8, и я столкнулся с тем, что кажется обычной операцией, которая не поддерживается функционально: "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.
6 ответов:
самый чистый подход "try services" с учетом текущего 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
.
это не красиво, но это будет работать:
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)
иOptional<Result>
. Если чтоOptional
содержит значениеv
, мы хотим получитьOptional.of(v)
, а если он пуст, мы хотим получитьserviceB(args)
. Промыть-повторите с большим количеством альтернатив.другие виды использования этого шаблон
.map(Stream::of).orElseGet(Stream::empty)
.map(Collections::singleton).orElseGet(Collections::emptySet)
возможно, это то, что вам нужно:получить значение из одного необязательного или другого
в противном случае, вы можете взглянуть на
Optional.orElseGet
. Вот пример того, что я думаю что вы после:result = Optional.ofNullable(serviceA().orElseGet( () -> serviceB().orElseGet( () -> serviceC().orElse(null))));
предполагая, что вы все еще на 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 опционально поддерживает правильный
or()
операция (так же, как JDK9), например:return serviceA(args) .or(() -> serviceB(args)) .or(() -> serviceC(args)) .or(() -> serviceD(args));
(где каждый из сервисов возвращает
com.google.common.base.Optional
, а неjava.util.Optional
).
это похоже на хорошую подгонку для сопоставления шаблонов и более традиционный интерфейс опций с некоторыми и никакими реализациями (например, в Javaslang,FunctionalJava) или лень может быть реализации в Циклоп-реагировать.Я автор этой библиотеки.
С Циклоп-реагировать вы также можете использовать конструкции шаблоны на типах JDK. Для опционного вы можете соответствовать на настоящем моменте и отсутствующие случаи через шаблон Visitor. это будет выглядеть примерно так -
import static com.aol.cyclops.Matchables.optional; optional(serviceA(args)).visit(some -> some , () -> optional(serviceB(args)).visit(some -> some, () -> serviceC(args)));