Код #Статьи

27 ноября, 2025

Лямбда-выражения и функциональные интерфейсы в Java

Что это такое, как функционирует и в каких целях используется?

Основы Python: бесплатный курс для всех уровней навыков Создание портфолио: 4 увлекательных проекта и интерактивное обучение Изучите возможности курса: что вас ждет в процессе обучения

Узнать больше

Создадим функции, которые будут вычислять сумму и произведение двух чисел:

Теперь давайте создадим единый метод, назовем его processTwoNumbers. Этот метод будет принимать два числовых параметра и код для их обработки.

private int processTwoNumbers(int a, int b, [код передаётся сюда])

При использовании метода sum третий параметр будет принимать в качестве аргумента операцию a+b, тогда как для метода mult этот параметр примет значение a*b.

Стоит отметить, что в качестве третьего аргумента можно использовать лишь код, который принимает два параметра определенного типа (в нашем случае это int) и выдает результат также нужного типа — int.

Следует каким-то образом уведомить компилятор о том, что необходимо ограничить будущих программистов от передачи некорректного кода (например, a+b+c).

В этом случае нам понадобится сигнатура метода, которая будет выступать в роли третьего параметра для нашего метода processTwoNumbers.

private int computeSumOfTwoIntegers(int a, int b, [method signature])

Однако как можно добавить третий параметр, не делая сигнатуру метода processTwoNumbers слишком громоздкой и трудночитаемой? Разработчики Java нашли элегантное решение этой задачи. Они разработали концепцию функциональных интерфейсов.

Определение функционального интерфейса: ключевые аспекты

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

Если метод принимает в качестве параметра функциональный интерфейс, то при его вызове необходимо передать блок кода в качестве одного из аргументов.

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

Чтобы прояснить ситуацию, давайте рассмотрим это на конкретном примере:

В Java существует ряд предопределённых функциональных интерфейсов, которые отличаются как количеством, так и типом параметров для входа и выхода. Примером такого интерфейса является ToIntBiFunction, о котором упоминалось ранее. Когда вы разрабатываете собственный функциональный интерфейс, крайне важно не забыть добавить аннотацию @FunctionalInterface. Эта аннотация служит для того, чтобы компилятор мог подтвердить, что создаваемый интерфейс действительно отвечает критериям функционального интерфейса.

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

  • Функция принимает два аргумента (T t, U u), где T и U обозначают возможность использования параметров различных типов. К примеру, это могут быть Long и String. Однако в нашем случае это излишне, поскольку оба аргумента имеют один и тот же тип — int.
  • возвращает результат в виде целого числа.

Вот что получится:

Интерфейс ToIntBiFunction подразумевает, что необходимо предоставить метод, который будет иметь ту же сигнатуру, что и метод, определенный внутри этого интерфейса.

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

Теперь мы подошли к теме лямбда-выражений.

Понимание лямбда-выражений: основные концепции

Это сжатая форма записи, вдохновленная λ-исчислением, предназначенная для передачи фрагментов кода в качестве аргументов в другую программу.

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

Таким образом, лямбда-выражение не является самостоятельным элементом, а служит для реализации метода, который задан в функциональном интерфейсе.

Если бы не было лямбд, каждый раз вызывание метода processTwoNumbers потребовало бы следующего подхода:

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

В приведённом примере все элементы, за исключением одной строки, избыточны. Функциональную нагрузку несёт лишь выражение return a + b, в то время как остальная часть кода представляет собой технические излишества. Их количество оказалось довольно значительным, даже для того чтобы просто реализовать операцию сложения двух чисел.

Именно здесь на помощь приходят лямбда-выражения. Благодаря им, процесс создания biFunction значительно упрощается и может быть выполнен всего за десять символов!

Лямбда-выражение имеет следующую структуру: (аргументы) -> (тело функции)

Наша лямбда примет следующий вид:

Би-функция, принимающая два целых числа и возвращающая их сумму, может быть определена следующим образом: ToIntBiFunction biFunction = (a, b) -> a + b;

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

Компилятор удостоверится, что лямбда-выражение соответствует требованиям функционального интерфейса, то есть принимает необходимое количество аргументов нужных типов. В данном случае мы используем функциональный интерфейс ToIntBiFunction. Его единственный абстрактный метод имеет сигнатуру, в которой указаны два параметра: (Integer a, Integer b).

К примеру, если вызвать метод таким образом, код не пройдет компиляцию, так как будет использован только один аргумент:

Существует множество способов записи лямбд. Мы изучили лишь один из них.

Применение лямбда-выражений в различных сферах.

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

Лямбды также находят применение в компараторах при осуществлении сортировки. Рассмотрим ситуацию, когда требуется упорядочить коллекцию на основе последней буквы каждого слова:

В большинстве случаев при использовании коллекций в сочетании со Stream API трудно обойтись без лямбда-выражений. В следующем примере мы сначала фильтруем поток данных на основе определенного значения (с помощью метода filter), затем преобразуем каждый элемент (метод map) и, наконец, собираем результаты в список (метод collect):