Связывание завершаемых фьючерсов на основе условий

#java #asynchronous #exception #completable-future #conditional-execution

#java #асинхронный #исключение #завершаемое-будущее #условное выполнение

Вопрос:

У меня есть куча методов, которые возвращают завершаемое будущее, и я хотел бы связать определенным образом

 package com.sandbox;

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.stream.IntStream;

public class SandboxFutures {

    public CompletableFuture<Integer> generateRandom(int min, int max) {
        return CompletableFuture.supplyAsync(() -> {
            if (min >= max) {
                throw new IllegalArgumentException("max must be greater than min");
            }

            Random r = new Random();
            return r.nextInt((max - min)   1)   min;
        });
    }

    public CompletableFuture<String> printEvenOrOdd(int result) {
        return CompletableFuture.supplyAsync(() -> {
            if (result % 2 == 0)
                return "Even";
            else
                return "Odd";
        });
    }

    public CompletableFuture<Integer> findFactorial(int evenNumber) {
        return CompletableFuture.supplyAsync(() -> {
            if (evenNumber <= 0) {
                return 0;
            }

            return IntStream.rangeClosed(2, evenNumber).reduce(1, (x,y) -> x*y);
        });
    }

    public CompletableFuture<Integer> convertToNearestEvenInteger(int oddNumber) {
        return CompletableFuture.supplyAsync(() -> {
           if (oddNumber <= 0) {
               return 2;
           }
           return oddNumber 1;
        });
    }

}
  

Я пытаюсь объединить их на основе следующих правил,

  1. Сгенерируйте случайное число от 1 до 100
  2. Если число четное, выведите Even , если оно нечетное, выведите Odd
  3. Если число четное, вызовите findFactorial со случайным числом
  4. Если число нечетное, найдите ближайшее четное через convertToNearestEvenInteger

Я не слишком понимаю, как выполнять условное связывание и обработку исключений. Могут быть полезны некоторые примеры или фрагменты кода.

Ответ №1:

Вы можете использовать thenCompose() :

 CompletableFuture<Integer> n = generateRandom(1, 100)
        .thenCompose(i -> printEvenOrOdd(i)
                .thenCompose(s -> s.equals("Even")
                        ? findFactorial(i)
                        : convertToNearestEvenInteger(i)));
System.out.println(n.get());
  

Однако, когда генерируются большие четные числа, ваш факториальный метод не может хранить ничего большего, чем int , поэтому вам нужно это обновить.

Ответ №2:

Способ printEvenOrOdd написания делает его более сложным, чем это должно быть. Проблема в том, что он не печатает слово «Четный» или «Нечетный», он возвращает его, что означает, что оригинал result потерян. Остальные шаги зависят от наличия фактического числа. Чтобы обойти это, вы могли бы использовать call printEvenOrOdd и использовать .thenApply(__ -> result) для последующего восстановления исходного номера. Это выглядело бы так:

 System.out.println(
    generateRandom(1, 100)
        .thenCompose(result ->
            printEvenOrOdd(result)
                .thenAccept(System.out::println)
                .thenApply(__ -> result)
        )
        .thenCompose(result ->
            result % 2 == 0
                ? findFactorial(result)
                : convertToNearestEvenInteger(result)
        )
        .join()
);
  

Лучшим решением было бы изменить определение printEvenOrOdd на что-то вроде:

 public CompletableFuture<Integer> printEvenOrOdd(int result) {
    return CompletableFuture.supplyAsync(() -> {
        System.out.println(result % 2 == 0 ? "Even" : "Odd");
        return resu<
    });
}
  

Это значительно упростило бы выполнение шагов 3 и 4:

 System.out.println(
    generateRandom(1, 100)
        .thenApply(this::printEvenOrOdd)
        .thenCompose(result ->
            result % 2 == 0
                ? findFactorial(result)
                : convertToNearestEvenInteger(result)
        )
        .join()
);