Что такое CompletableFuture в Java и для чего он используется?

«Что такое CompletableFuture в Java и для чего он используется?» — вопрос из категории Java Core, который задают на 26% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

CompletableFuture<T> — это класс, появившийся в Java 8, который представляет собой отложенный результат асинхронной операции. Он сочетает в себе возможности Future (представление будущего результата) и CompletionStage (возможность строить цепочки и комбинировать асинхронные задачи).

Ключевые возможности:

  1. Явное завершение: Вы можете завершить CompletableFuture вручную с результатом (complete(T value)) или исключением (completeExceptionally(Throwable ex)).
  2. Цепочки вызовов (chaining): Позволяет описывать поток обработки: "когда завершится эта задача, сделай то, затем другое".
  3. Комбинация нескольких Future: Объединение результатов двух и более независимых асинхронных задач.
  4. Гибкая обработка исключений прямо в цепочке.
  5. Асинхронное выполнение с использованием пулов потоков (ForkJoinPool.commonPool() по умолчанию или своего).

Основные методы для построения цепочек:

  • thenApply(Function) — преобразует результат (синхронно).
  • thenApplyAsync(Function) — преобразует результат асинхронно.
  • thenAccept(Consumer) — потребляет результат без возврата значения.
  • thenCompose(Function) — "плоское" преобразование (композиция Future).
  • thenCombine(CompletionStage, BiFunction) — объединяет результаты двух Future.
  • exceptionally(Function) — обрабатывает исключение, возвращая резервное значение.
  • handle(BiFunction) — обрабатывает и результат, и исключение.

Практический пример: параллельный вызов двух сервисов и объединение результата.

import java.util.concurrent.CompletableFuture;

public class OrderService {
    // Имитация асинхронных вызовов
    CompletableFuture<Double> getPriceAsync(String productId) {
        return CompletableFuture.supplyAsync(() -> {
            // Долгий вызов к Price Service
            return 99.99;
        });
    }

    CompletableFuture<Double> getDiscountAsync(String userId) {
        return CompletableFuture.supplyAsync(() -> {
            // Долгий вызов к Discount Service
            return 0.1; // 10% скидка
        });
    }

    public CompletableFuture<Double> calculateTotalAsync(String productId, String userId) {
        CompletableFuture<Double> priceFuture = getPriceAsync(productId);
        CompletableFuture<Double> discountFuture = getDiscountAsync(userId);

        // Комбинируем результаты, когда оба Future завершатся
        return priceFuture.thenCombine(discountFuture, (price, discountRate) -> {
            return price * (1 - discountRate); // Итоговая цена со скидкой
        }).exceptionally(ex -> { // Обработка ошибок в любой из задач
            System.err.println("Ошибка расчета: " + ex.getMessage());
            return 0.0; // Возвращаем значение по умолчанию
        });
    }

    public static void main(String[] args) throws Exception {
        OrderService service = new OrderService();
        CompletableFuture<Double> totalFuture = service.calculateTotalAsync("prod1", "user42");

        // Блокирующее ожидание результата (в реальном коде лучше использовать thenAccept)
        Double total = totalFuture.get();
        System.out.println("Итоговая цена: " + total); // Выведет: Итоговая цена: 89.991
    }
}

Использование: CompletableFuture — основной инструмент для неблокирующего асинхронного программирования в современных Java-приложениях, особенно в микросервисных архитектурах и высоконагруженных веб-сервисах.