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

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

Открыта

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 лекция
Недоступна
Текстовый Конвертер с Удобным Выводом
Текстовый Конвертер с Удобным Выводом
Комментарии (12)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Gans Electro
Уровень 59
27 июня, 13:15
Этой аннотации не было в старом курсе
Roman Preminin
Уровень 18
20 июня, 10:35
Читаю комментарии - ну да вначале сложновато понять . Я написал сначала все решения через переопределение интерфейса. А потом переписал в одну строку через лямбду (Понимания как ее писать хватает в лекции) и просто КАЙФ!!! Особенно когда кажется что понял! ))
САН САНЫЧ
Уровень 20
26 февраля, 16:42
про анонимный класс ничего не знаем.
aleed
Уровень 20
6 апреля, 14:05
Был же блок про анонимный класс
Anonymous #3534203
Уровень 20
7 ноября 2025, 13:34
Не совсем понятно, материал про функциональные интерфейсы, потом берется лямбда о которой расскажем позже и задания с лямбдой. Странно как то?
Xaxatumba
Уровень 38
12 ноября 2025, 11:56
Потому что: "Если у интерфейса есть только один метод, переменной этого типа-интерфейса можно присвоить значение, заданное лямбда-выражением (лямбда-функцией). Такие интерфейсы стали называть функциональными интерфейсами (после добавления в Java поддержки лямбда-функций)." Это фишка JavaRush накидаем информации, а вы как реальный программист сами разбирайтесь нада оно вам или нет )
Ksanders
Уровень 32
1 декабря 2025, 17:26
Классика, несколько раз в лекции упоминается некая "лямбда" о которой расскажем позже - в итоге в первой в каждой же задаче лямбда 😃
Олег
Уровень 40
8 мая, 12:25
Согласен. не корректно. это как будто задавать домашку по математике из 4й четверти , а учимся пока в первой.
Александр
Уровень 50
31 октября 2025, 12:04
Доходчиво объяснено. Переварить, конечно, требуется, но составителю данной лекции большая благодарность.
Grrbrr7 System Engineer
18 января, 16:17
Сомнительно, но окэй
Anton Pohodin
Уровень 27
10 октября 2025, 05:19
Ну, хоть что-то новенькое рассказали)
nastya_zhadan
Уровень 66
23 сентября 2025, 10:31
Спасибо за интересную и полезную лекцию)