Логическое ИЛИ нескольких необязательных экземпляров?

1

Существует ли более элегантный способ представления следующего алгоритма с использованием Java API ядра?

Optional<Float> input = Optional.of(A);
Optional<Float> output = input.map(function1).map(function2);
if (!output.isPresent())
{
  output = input.map(function1).map(function2);
  if (!output.isPresent())
  {
    output = input.map(function3).map(function4);
      if (!output.isPresent())
        output = input.map(function5).map(function6);
  }
}

То есть вместо вложенности нескольких вызовов isPresent() я хотел бы вызвать метод, подобный map() но это будет вызвано только в том случае, если значение отсутствует.

В идеальном мире я хочу вызывать что-то вроде:

Optional<Float> output = input.or(input.map(function1).map(function2), input.map(function2).map(function3), input.map(function4).map(function5));

Это то, что у меня есть до сих пор, но можем ли мы сделать лучше?

public class Optionals
{
    /**
     * @param <T>       the type of value returned by the Optionals
     * @param suppliers one or more suppliers that return a supplier
     * @return the first Optional that {@link Optional#isPresent() isPresent()}
     */
    public static <T> Optional<T> or(Supplier<Optional<T>>... suppliers)
    {
        for (Supplier<Optional<T>> supplier: suppliers)
        {
            Optional<T> candidate = supplier.get();
            if (candidate.isPresent())
                return candidate;
        }
        return Optional.empty();
    }

    /**
     * Prevent construction.
     */
    private Optionals()
    {
    }
}
  • 0
    Я бы переименовал вашу or функцию в firstOf и просто использовал ее. Сдается мне как хорошее решение.
Теги:
java-stream
optional

4 ответа

0
Лучший ответ

Мне не нравится схема Supplier в этом случае и, вероятно, предпочла бы такой подход:

public static void main(String[] args) {

    Float x = 1.0f;
    final Optional<Float> input = Optional.of(x);
    Optional<Float> output = firstPresent(input, (a) -> a.map(FUNCTION1).map(FUNCTION2),
                                     (a) -> a.map(FUNCTION3).map(FUNCTION4),
                                     (a) -> a.map(FUNCTION5).map(FUNCTION6));
}

@SafeVarargs
public static <I,O> Optional<O> firstPresent(Optional<I> input, Function<Optional<I>, Optional<O>>... functions) {
    for (Function<Optional<I>, Optional<O>> function : functions) {
        final Optional<O> output = function.apply(input);
        if(output.isPresent()) {
            return output;
        }
    }
    return Optional.empty();
}
  • 0
    В моем случае Supplier захватывает input из внешнего блока кода, но да, это более явно.
  • 0
    @Gili, таким образом, вы также можете использовать лямбда-функции / функции более легко. Поставщики должны быть построены для каждого входа.
Показать ещё 1 комментарий
2

Optional имеет метод orElse; если вы вызовете это, он вернет значение в Optional если оно не пустое, или значение, которое вы передаете orElse если оно пустое.

Таким образом, вы можете использовать вложенные вызовы orElse, например:

Float output = input.map(function1).map(function2)
    .orElse(input.map(function3).map(function4)
        .orElse(input.map(function5).map(function6)
            .orElse(0.0f)));

Обратите внимание, что конечный результат в этом случае не является Optional<Float> а Float. Если вы не хотите, чтобы возвращаемое значение по умолчанию, например, 0.0f когда ни одно из сопоставлений не возвращало значение, вы могли бы, например, сделать это исключение:

Float output = input.map(function1).map(function2)
    .orElse(input.map(function3).map(function4)
        .orElse(input.map(function5).map(function6)
            .orElseThrow(() -> new IllegalStateException("No result"))));
  • 1
    Не приведет ли это всегда к оценке функции 3 до функции 6?
  • 1
    @TheConstructor, вы можете использовать orElseGet, чтобы вместо этого передавать лямбду и избежать энергичной оценки.
Показать ещё 1 комментарий
1
return Arrays.stream(suppliers).filter(supplier -> supplier.get().isPresent()).toArray();
  • 0
    не проверял это. Java 8 лямбда-выражений с использованием потоков массива.
  • 0
    Это оценят все поставщики, а это не то, что я хочу. Я хочу вернуться, чтобы оценить поставщиков по порядку и остановиться, как только один из них вернет непустое значение.
Показать ещё 1 комментарий
0

Способ реализации "множественного условия" логического И с помощью предикатов Java8 (Йоси Лев):

public static void main(String[] args) {
 Optional<String> stringOptional = Optional.of( "this is a string" );
 Predicate<String> isNotNull  = s-> s != null;
 Predicate<String> isSizeGT0  = s-> s.length() > 0 ;
 Predicate<String> isSizeGT20 = s-> s.length() > 20 ;
 // calling isOk() with all required Predicates to be approved:
 System.out.println(isOk(stringOptional, isNotNull, isSizeGT0, isSizeGT20));
}//main()

@SafeVarargs
private static <I> boolean isOk(Optional<I> inp, Predicate<I>... predicates){
 // Enforce all predicates on input..
 boolean b = Arrays.stream( predicates ).allMatch( pr->pr.test( inp.get()));
 return b; 
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню