Где на практике применяются функциональные интерфейсы в Java?

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

Ответ

Функциональные интерфейсы (имеющие ровно один абстрактный метод) — основа для лямбда-выражений и широко применяются в современных Java API.

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

  1. Stream API: Для декларативной обработки коллекций.

    • Predicate<T> — фильтрация: .filter(x -> x > 0)
    • Function<T,R> — преобразование: .map(String::toUpperCase)
    • Consumer<T> — терминальная операция: .forEach(System.out::println)
    • Supplier<T> — генерация потоков: Stream.generate(() -> "item")
  2. Многопоточность и асинхронность:

    • Runnable и Callable<V> — задачи для ExecutorService.
      executor.submit(() -> System.out.println("Task")); // Runnable
  3. Обработка событий и паттерны проектирования:

    • В UI-фреймворках (обработчики нажатий кнопок).
    • Паттерн Стратегия (Strategy) для инкапсуляции алгоритмов.
      public void processData(List<String> data, Function<String, String> processor) {
          data.stream().map(processor)...
      }
  4. Работа с Optional и отложенные вычисления:

    • Supplier<T> в Optional.orElseGet(supplier).
  5. Сортировка и сравнение:

    • Comparator<T> — часто используется с лямбдами.
      list.sort((a, b) -> a.getName().compareTo(b.getName()));

Преимущество: Функциональные интерфейсы позволяют передавать поведение как данные, делая код более гибким, переиспользуемым и лаконичным.