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

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

Ищете способ привязки опций, чтобы возвращался первый, который присутствует. Если ни один не присутствует, Optional.empty() должен быть возвращен.

Предполагая, что у меня есть несколько методов:

Optional<String> find1()

Я пытаюсь связать их:

Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );

но, конечно, это не работает, потому что orElse ожидает, что значение и orElseGet ожидает a Supplier.

4b9b3361

Ответ 1

Используйте поток:

Stream.of(find1(), find2(), find3())
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

Если вам нужно оценить методы поиска лениво, используйте функции поставщика:

Stream.of(this::find1, this::find2, this::find3)
    .map(Supplier::get)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

Ответ 2

Вы можете сделать это следующим образом:

Optional<String> resultOpt = Optional.of(find1()
                                .orElseGet(() -> find2()
                                .orElseGet(() -> find3()
                                .orElseThrow(() -> new WhatEverException()))));

Хотя я не уверен, что он улучшает читаемость ИМО. Guava обеспечивает способ подключения опций:

import com.google.common.base.Optional;

Optional<String> resultOpt = s.find1().or(s.find2()).or(s.find3());

Это может быть другой альтернативой вашей проблеме, но не использует стандартный дополнительный класс в JDK.

Если вы хотите сохранить стандартный API, вы можете написать простой служебный метод:

static <T> Optional<T> or(Optional<T> first, Optional<T> second) {
    return first.isPresent() ? first : second;
}

а затем:

Optional<String> resultOpt = or(s.find1(), or(s.find2(), s.find3()));

Если у вас есть много вариантов для цепей, возможно, лучше использовать подход Stream, как уже упоминалось ранее.

Ответ 3

Вдохновленный ответом Саули, можно использовать метод flatMap().

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
  .findFirst();

Преобразование необязательного в поток является громоздким. По-видимому, это будет исправлено с JDK9. Таким образом, это можно записать как

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(Optional::stream)
  .findFirst();

Обновление после выпуска Java 9

Хотя исходный вопрос касался Java 8, Optional::or был введен в Java 9. С его помощью проблема может быть решена как следует

Optional<String> result = find1()
  .or(this::find2)
  .or(this::find3);

Ответ 4

Возможно, один из

    public <T> Optional<? extends T> firstOf(Optional<? extends T> first, @SuppressWarnings("unchecked") Supplier<Optional<? extends T>>... supp) {
        if (first.isPresent()) return first;
        for (Supplier<Optional <? extends T>> sup : supp) {
            Optional<? extends T> opt = sup.get();
            if (opt.isPresent()) {
                return opt;
            }
        }
        return Optional.empty();
    }

    public <T> Optional<? extends T> firstOf(Optional<? extends T> first, Stream<Supplier<Optional<? extends T>>> supp) {
        if (first.isPresent()) return first;
        Stream<Optional<? extends T>> present = supp.map(Supplier::get).filter(Optional::isPresent);
        return present.findFirst().orElseGet(Optional::empty);
    }

сделает.

Первый выполняет итерацию по множеству поставщиков. Возвращается первая непустая Optional<>. Если мы его не найдем, мы возвращаем пустой Optional.

Второй делает то же самое с Stream of Suppliers, который проходит, каждый просил (лениво) их значение, которое затем фильтруется для пустых Optional s. Возвращается первая непустая, или, если она не существует, пустая.