JavaRush /Курсы /JAVA 25 SELF /Функциональные интерфейсы: @FunctionalInterface

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

JAVA 25 SELF
20 уровень , 3 лекция
Открыта

1. Введение

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

Почему только один метод?

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

Примеры из стандартной библиотеки

  • Runnable — для задач в потоках (void run())
  • Callable<V> — для задач, которые возвращают результат (V call())
  • Comparator<T> — для сравнения объектов (int compare(T o1, T o2))
  • Consumer<T> — «потребитель» значения (void accept(T t))
  • Supplier<T> — «поставщик» значения (T get())
  • Function<T, R> — функция из T в R (R apply(T t))
  • Predicate<T> — проверка условия (boolean test(T t))

Вот, например, интерфейс Runnable:

public interface Runnable {
    void run();
}

А вот интерфейс Comparator:

public interface Comparator<T> {
    int compare(T o1, T o2);
    // ... есть еще default- и static-методы, но абстрактный метод только один!
}

Важный момент: default- и static-методы не считаются абстрактными, поэтому их может быть сколько угодно!

2. Аннотация @FunctionalInterface

Java — строгая и принципиальная дама. Чтобы не было путаницы, она позволяет явно пометить интерфейс как функциональный с помощью аннотации @FunctionalInterface. Это как наклейка «Работает только с одной кнопкой!» — чтобы никто не добавил лишнего.

@FunctionalInterface
public interface Operation {
    int apply(int a, int b);
}

Теперь если вы вдруг забудете и добавите второй абстрактный метод, компилятор тут же сообщит об ошибке:

@FunctionalInterface
public interface Oops {
    void doIt();
    void doSomethingElse(); // Ошибка! Два абстрактных метода
}

Обязательна ли аннотация?

Нет, это не обязательно. Интерфейс будет функциональным и без неё, если содержит ровно один абстрактный метод. Но с аннотацией вы явно показываете своё намерение и защищаете себя от случайных ошибок.

Можно ли добавлять default и static методы?

Да, можно! Главное, чтобы был только один абстрактный метод. Все остальные методы могут быть default или static, сколько душе угодно.

Пример:

@FunctionalInterface
public interface FancyOperation {
    int apply(int a, int b);

    default void printInfo() {
        System.out.println("Я — fancy операция!");
    }

    static void description() {
        System.out.println("Функциональный интерфейс для арифметики.");
    }
}

3. Примеры объявления и использования

Допустим, вы хотите описать операцию над двумя числами. Вот так это делается:

@FunctionalInterface
public interface Operation {
    int apply(int a, int b);
}

Теперь этот интерфейс можно реализовать по-разному.

Реализация через обычный класс

public class SumOperation implements Operation {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
}

Использование:

Operation sum = new SumOperation();
System.out.println(sum.apply(2, 3)); // 5

Реализация через анонимный класс

Operation multiply = new Operation() {
    @Override
    public int apply(int a, int b) {
        return a * b;
    }
};
System.out.println(multiply.apply(2, 3)); // 6

Замечание про лямбды

Начиная с Java 8, такие интерфейсы удобно реализовывать через лямбда-выражения — более короткий синтаксис. Мы изучим лямбды через пару лекций, поэтому пока достаточно знать: функциональные интерфейсы созданы именно для того, чтобы работать с ними максимально удобно.

4. Практика: напиши свой функциональный интерфейс

Задание 1. Сделай свой Action!

Создайте интерфейс Action, который принимает строку и ничего не возвращает. Реализуйте его через анонимный класс, который печатает строку в верхнем регистре.

@FunctionalInterface
interface Action {
    void act(String s);
}

public class ActionDemo {
    public static void main(String[] args) {
        Action shout = new Action() {
            @Override
            public void act(String text) {
                System.out.println(text.toUpperCase());
            }
        };

        shout.act("я учу java!"); // Я УЧУ JAVA!
    }
}

(Позже мы увидим, как это можно записать короче с помощью лямбда-выражений.)

Задание 2. Фильтр чисел

Создайте интерфейс NumberPredicate с методом boolean test(int n). Реализуйте проверку на чётность с помощью анонимного класса.

@FunctionalInterface
interface NumberPredicate {
    boolean test(int n);
}

public class PredicateDemo {
    public static void main(String[] args) {
        NumberPredicate isEven = new NumberPredicate() {
            @Override
            public boolean test(int n) {
                return n % 2 == 0;
            }
        };

        System.out.println(isEven.test(4)); // true
        System.out.println(isEven.test(7)); // false
    }
}

Задание 3. Используем стандартные интерфейсы

Вместо собственного интерфейса можно использовать готовый Predicate<Integer>:

import java.util.function.Predicate;

Predicate<Integer> isPositive = new Predicate<Integer>() {
    @Override
    public boolean test(Integer x) {
        return x > 0;
    }
};

System.out.println(isPositive.test(10)); // true
System.out.println(isPositive.test(-5)); // false

Таблица: функциональные интерфейсы из стандартной библиотеки

Интерфейс Метод Описание Пример применения
Runnable
void run()
Задача без аргументов и результата Потоки, таймеры
Callable<V>
V call()
Задача с результатом Потоки, ExecutorService
Comparator<T>
int compare(T, T)
Сравнение двух объектов Сортировка коллекций
Consumer<T>
void accept(T)
"Потребитель" значения Перебор коллекции
Supplier<T>
T get()
"Поставщик" значения Ленивая инициализация, генерация данных
Function<T, R>
R apply(T)
Функция из T в R Преобразование данных
Predicate<T>
boolean test(T)
Проверка условия Фильтрация коллекций

5. Типичные ошибки при работе с функциональными интерфейсами

Ошибка №1: добавили второй абстрактный метод. Если в интерфейсе больше одного абстрактного метода, он перестаёт быть функциональным. Компилятор (особенно с @FunctionalInterface) тут же сообщит об ошибке.

Ошибка №2: забыли, что default- и static-методы не считаются абстрактными. Можно смело добавлять их в функциональный интерфейс — это не нарушит правило «одного абстрактного метода».

Ошибка №3: неверно реализовали сигнатуру метода. Например, интерфейс требует два аргумента, а вы написали метод только с одним. Всегда проверяйте сигнатуры.

Ошибка №4: не используете @FunctionalInterface и случайно портите интерфейс. Если не пометить интерфейс аннотацией, можно случайно добавить лишний метод — и потом долго искать, почему код не работает. Лучше всегда добавлять аннотацию для явности.

1
Задача
JAVA 25 SELF, 20 уровень, 3 лекция
Недоступна
Ваш Персональный Нотификатор
Ваш Персональный Нотификатор
1
Задача
JAVA 25 SELF, 20 уровень, 3 лекция
Недоступна
Игровой Чекер: Высокие Баллы
Игровой Чекер: Высокие Баллы
1
Задача
JAVA 25 SELF, 20 уровень, 3 лекция
Недоступна
Валидатор Текста: Проверка Длины Строки
Валидатор Текста: Проверка Длины Строки
1
Задача
JAVA 25 SELF, 20 уровень, 3 лекция
Недоступна
Текстовый Конвертер с Удобным Выводом
Текстовый Конвертер с Удобным Выводом
Комментарии (7)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anonymous #3534203 Уровень 2
7 ноября 2025
Не совсем понятно, материал про функциональные интерфейсы, потом берется лямбда о которой расскажем позже и задания с лямбдой. Странно как то?
Xaxatumba Уровень 38
12 ноября 2025
Потому что: "Если у интерфейса есть только один метод, переменной этого типа-интерфейса можно присвоить значение, заданное лямбда-выражением (лямбда-функцией). Такие интерфейсы стали называть функциональными интерфейсами (после добавления в Java поддержки лямбда-функций)." Это фишка JavaRush накидаем информации, а вы как реальный программист сами разбирайтесь нада оно вам или нет )
Ksanders Уровень 32
1 декабря 2025
Классика, несколько раз в лекции упоминается некая "лямбда" о которой расскажем позже - в итоге в первой в каждой же задаче лямбда 😃
Александр Уровень 50
31 октября 2025
Доходчиво объяснено. Переварить, конечно, требуется, но составителю данной лекции большая благодарность.
Grrbrr7 Уровень 20
18 января 2026
Сомнительно, но окэй
Anton Pohodin Уровень 26
10 октября 2025
Ну, хоть что-то новенькое рассказали)
nastya_zhadan Уровень 66
23 сентября 2025
Спасибо за интересную и полезную лекцию)