В каких случаях применяется интерфейс Callable в Java?

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

Ответ

Callable применяется для представления асинхронной задачи, которая должна возвращать результат и может выбрасывать проверяемые исключения. Это более мощная альтернатива Runnable.

Основные области применения:

  1. Исполнение в пуле потоков (ExecutorService): Передача задач, требующих возврата результата.

    ExecutorService executor = Executors.newFixedThreadPool(2);
    Callable<Integer> computeTask = () -> {
        // Долгие вычисления
        return 42;
    };
  2. Получение результата через Future: Метод ExecutorService.submit(Callable) возвращает Future<V>, который используется для асинхронного получения результата или контроля над задачей.

    Future<Integer> future = executor.submit(computeTask);
    // ... делать другую работу ...
    Integer result = future.get(); // Блокирует поток, пока задача не завершится
  3. Обработка исключений внутри задачи: Callable.call() объявляет throws Exception, что позволяет корректно пробрасывать исключения из потока в вызывающий код.

    Callable<String> riskyTask = () -> {
        if (error) throw new IOException("Read failed");
        return "Success";
    };
    // Исключение будет обернуто в ExecutionException при вызове future.get()

Главное отличие от Runnable: Runnable.run() не возвращает значения и не может бросать проверяемые исключения.