Функциональные интерфейсы

Базовый Java
≈ 2 мин чтения

Интерфейс задаёт форму поведения

Lambda-выражению нужен тип. В Java таким типом служит функциональный интерфейс — интерфейс ровно с одним абстрактным методом. Параметры этого метода определяют параметры lambda, а возвращаемый тип — то, что lambda должна вернуть.

@FunctionalInterface
interface TextRule {
    boolean test(String value);
}

TextRule longText = text -> text.length() >= 5;
System.out.println(longText.test("Java"));

У интерфейса могут быть default- и static-методы: они не добавляют новых абстрактных требований и не мешают интерфейсу оставаться функциональным. Аннотация @FunctionalInterface не обязательна, но помогает компилятору проверить это намерение.

Главные интерфейсы из java.util.function

Для типовых сценариев свои интерфейсы создавать не нужно. Пакет java.util.function содержит готовые варианты:

  • Predicate<T> — получает значение и возвращает boolean;
  • Function<T, R> — преобразует значение типа T в R;
  • Consumer<T> — получает значение и ничего не возвращает;
  • Supplier<T> — ничего не получает и создаёт или предоставляет значение;
  • UnaryOperator<T> и BinaryOperator<T> — частные случаи функций, где вход и результат имеют один тип.
Predicate<String> isLong = text -> text.length() >= 5;
Function<String, Integer> length = String::length;
Consumer<String> print = System.out::println;
Supplier<String> message = () -> "Ready";

Названия хорошо подсказывают смысл. Predicate отвечает на вопрос, Function преобразует, Consumer потребляет, а Supplier предоставляет значение. Эти интерфейсы постоянно встречаются в Stream API и других частях стандартной библиотеки.

Один интерфейс — разные реализации

Переменная функционального интерфейса может хранить lambda, ссылку на метод или обычный объект класса, который реализует этот интерфейс. Для вызывающего кода разницы нет: он работает с методом интерфейса.

Predicate<String> startsWithJ = text -> text.startsWith("J");

if (startsWithJ.test("Java")) {
    System.out.println("match");
}
Не создавай собственный функциональный интерфейс без причины. Если сигнатура хорошо описывается стандартным Predicate, Function, Consumer или Supplier, обычно лучше использовать готовый тип.