Лямбда-функции в Java: что это и зачем использовать их в коде
Программирование • 06 августа 2024 • 5 мин чтения
Лямбда-функции в Java: что это и зачем использовать их в коде
Разбираемся, что представляют собой lambda-функции (часто их называют «lambda-выражения» или просто «лямбда»), какими они бывают и как использовать их в Java. Рассказываем о плюсах и минусах, а также приводим примеры лямбда-функций.
Концепция лямбда-выражений (lambda) появилась в Java с версии 1.8 и ввела в этот язык в элементы функционального программирования. Изначально Java построен на императивном подходе, в рамках которого разработчик описывает конкретные действия, которые должна выполнить программа. А суть функционального подхода состоит в описании не процесса, а результата, который необходимо получить. Результат выводится в виде функций, но при этом они необязательно выполняются сразу, а вычисляются при необходимости.
Лямбда-выражение — одно из понятий функционального программирования. Такие выражения также иногда называют функциями первого порядка или анонимными функциями. Лямбда-выражение представляет собой блок кода, запоминающий контекст вокруг себя в момент создания. Если раньше в коде программист присвоил определённые значения переменным a и b, lambda их помнит и использует для вычислений, к примеру для определения гипотенузы по конкретным значениям катетов a и b.
Обычные функции в языке «Джава» описываются в классах как методы и всегда используют внутри себя только те значения, которые разработчик запишет в экземпляр этого класса. Например, если в классе добавить определённые значения переменной, метод будет работать именно с ними. А лямбда-выражение помнит только те значения, с которыми оно было создано. Допустим, в методе есть переменная и лямбда-выражения в цикле. Каждая новая lambda будет помнить новое значение.
Синтаксис лямбда-функции в Java состоит из аргументов и lambda-выражений. Шаблон лямбда-функции выглядит так:
В скобках указываются параметры выражения, дальше идёт лямбда-оператор ->, после которого записывается само выражение или целый блок команд
С помощью лямбда-выражений можно реализовать функциональный интерфейс, а также сделать код проще и эффективнее. Возьмём для примера метод для возвращения числа П:
double getPiValue() {
return 3.1415;
}
Так его можно записать lambda-выражением:
() -> 3.1415
Из коробки, то есть по умолчанию, Java поддерживает два варианта lambda:
1. Одиночное (single-expression body). Включает в себя только единственное выражение и применяется при передаче функции в качестве аргумента к методу. Допустим, она пригодится, если нужно рассчитать сумму:
(x) -> x + 1.
2. Блок кода (multi-statement body). Его также называют многострочным выражением. Это lambda с несколькими операторами, которая даёт возможность выполнять разные операции внутри одного интерфейса. Допустим, оно пригодится для поиска максимального простого числа в группе:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
.filter(x -> {
if (n <= 1) {
return false;
}
for (int i = 2; i <= Math.sqrt(n); i++) {
if (n % i == 0) {
return false;
}
}
return true;
.max((x, y) -> x.compareTo(y))
.ifPresent(System.out::println);
Lambda-функции применяются в Java в нескольких направлениях:
● Функциональные интерфейсы. Лямбды полезны для создания интерфейсов с одним абстрактным методом. В результате есть возможность создавать анонимные классы без необходимости определения нового класса. Раньше в качестве аналога лямбд в Java применялись анонимные классы, теперь это не требуется.
Function<Double, Long> roundFn = d -> Math.round(d)
● Stream API. Он представляет собой группу классов и интерфейсов, с помощью которых можно работать с коллекциями объектов. По сути, Stream API основан на lambda-выражениях — к примеру, их используют как условия или функции отображения. Допустим, с помощью lambda можно вывести чётные числа:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
.filter(x -> x % 2 == 0)
.forEach(System.out::println);
● Асинхронное программирование в SpringBoot. За счёт того, что lambda запоминает контекст, запросы обрабатываются быстрее, освобождая потоки. К примеру, запросы futures, future, listenable future, completable future пишутся в лямбда-выражениях.
Научиться использовать lambda и другие функции в коде можно на курсе «Java-разработчик». Во время обучения студенты осваивают Java Core и фреймворк Spring, учатся работать с алгоритмами, структурами и базами данных, изучают Unit- и Mock-тестирование. По итогам курса можно собрать портфолио и найти первую работу по специальности.
Рассмотрим плюсы и минусы лямбда-выражений в программировании на Java.
Возьмём для примера for-each — цикл в Java, помогающий в работе с коллекциями. Это типичный Consumer — интерфейс, принимающий параметр, но не возвращающий результат. Если в цикле нужно вывести элементы списка на экран, пригодится lambda-выражение.
List<String> trees = Arrays.asList("Дуб", "Берёза", "Тополь");
trees.forEach(tree -> System.out.println(tree));
Причём это не совсем честная лямбда-функция, потому что она всегда должна возвращать какой-то результат.
Другой пример — Producer, функция в Stream API, которая не принимает никакого значения, но возвращает какой-то результат. Её также называют генераторной функцией. Допустим, поток должен сгенерировать число 42 и вывести десять элементов на экран. В решении этой задачи поможет лямбда-выражение:
IntStream stream = Stream.generate(() -> 42).limit(10);
stream.forEach(System.out::println);
Ещё lambda в Java используют в интерфейсе Function. Он может принимать и выдавать значение, но разного типа. К примеру, Function<Integer, String> принимает цифру, а возвращает слово, соответствующее этой цифре. Эта функция переведёт любое число меньше 100 в его прописную форму:
public static void main(String[] args) {
//используем словари русских цифр и чисел в специальной lambda-функции
Function<Integer, String> numberToWords = number ->
numberWordsMap.containsKey(number) ? numberWordsMap.get(number) :
tensWordsMap.containsKey(number) ? tensWordsMap.get(number) :
tensWordsMap.get((number / 10) * 10) + " " + numberWordsMap.get(number % 10);
int number = 21;
System.out.println(numberToWords.apply(number)); // На выходе получим: двадцать один
}
Кроме того, лямбда-выражения пригодятся при реализации интерфейса Predicate, который возвращает true или false в ответ на значение. Он применяется для фильтрации данных из потока. Допустим, можно отфильтровать строку так, чтобы она начиналась с элемента на определённую букву.
List<String> trees = Arrays.asList("Дуб", "Берёза", "Тополь");
trees.stream()
.filter(tree -> tree.startsWith("Т"))
.forEach(System.out::println);
Таким образом, с помощью лямбда-выражений пишется оптимальный код на Java. Без них программистам приходится вручную описывать действия и фильтры в цикле, копировать списки или делать вложенные циклы. Это усложняет код и требует больше усилий со стороны разработчика.
Совет эксперта
Читать также: