Lambda-выражения в Java
Привет, Хабр! Представляю вашему вниманию перевод статьи «Java Lambda Expressions» автора www.programiz.com.
Введение
В этой статье, с помощью примеров, мы изучим lambda-выражения в Java, их использование с функциональными интерфейсами, параметризированными функциональными интерфейсами и Stream API.
Лямбда выражения были добавлены в Java 8. Их основная цель – повысить читабельность и уменьшить количество кода.
Но, прежде чем перейти к лямбдам, нам необходимо понимать функциональные интерфейсы.
Что же такое функциональный интерфейс?
Если интерфейс в Java содержит один и только один абстрактный метод, то он называется функциональным. Этот единственный метод определяет назначение интерфейса.
Например, интерфейс Runnable из пакета java.lang является функциональным, потому, что он содержит только один метод run().
Пример 1: объявление функционального интерфейса в java
import java.lang.FunctionalInterface; @FunctionalInterface public interface MyInterface < // один абстрактный метод double getValue(); >
В приведенном выше примере, интерфейс MyInterface имеет только один абстрактный метод getValue(). Значит, этот интерфейс — функциональный.
Здесь мы использовали аннотацию FunctionalInterface, которая помогает понять компилятору, что интерфейс функциональный. Следовательно, не позволяет иметь более одного абстрактного метода. Тем не менее, мы можем её опустить.
В Java 7, функциональные интерфейсы рассматривались как Single Abstract Methods (SAM). SAM обычно реализовывались с помощью анонимных классов.
Пример 2: реализация SAM с помощью анонимного класса в java
public class FunctionInterfaceTest < public static void main(String[] args) < // анонимный класс new Thread(new Runnable() < @Override public void run() < System.out.println("Я только что реализовал функциональный интерфейс Runnable.") >>).start(); > >
Результат выполнения:
Я только что реализовал функциональный интерфейс Runnable.
В этом примере, мы принимаем анонимный класс для вызова метода. Это помогало писать программы с меньшим количеством строк кода в Java 7. Однако, синтаксис оставался достаточно сложным и громоздким.
Java 8 расширила возможности SAM, сделав шаг вперед. Как мы знаем, функциональный интерфейс содержит только один метод, следовательно, нам не нужно указывать название метода при передаче его в качестве аргумента. Именно это и позволяет нам lambda-выражения.
Введение в лямбда-выражения
Лямбда-выражения, по сути, это анонимный класс или метод. Лямбда-выражение не выполняется само по себе. Вместо этого, оно используется для реализации метода, определенного в функциональном интерфейсе.
Как записать лямбда-выражение в Java?
В Java, лямбда-выражения имеют следующий синтаксис:
(parameter list) -> lambda body
Здесь мы использовали новый оператор (->) — лямбда-оператор. Возможно, синтаксис кажется немного сложным. Давайте разберем пару примеров.
Предположим, у нас есть такой метод:
double getPiValue()
Мы можем записать его, используя лямбда, как:
() -> 3.1415
Этот метод не имеет никаких параметров. Следовательно, левая часть выражения содержит пустые скобки. Правая сторона – тело лямбда-выражения, которое определяет его действие. В нашем случае, возвращается значение 3.1415.
Типы лямбда-выражений
В Java, тело лямбды может быть двух типов.
1. Однострочные
() -> System.out.println("Lambdas are great");
2. Блочные (многострочные)
Этот тип позволяет лямбда-выражению иметь несколько операций внутри себя. Эти операции должны быть помещены в фигурные скобки, после которых необходимо ставить точку с запятой.
Примечание: многострочные лямбда-выражения, всегда должны иметь оператор return, в отличии от однострочных.
Пример 3: лямбда-выражение
Давайте напишем Java программу, которая бы возвращала значение Pi, используя лямбда-выражение.
Как говорилось ранее, лямбда-выражение не выполняется само собой. Скорее, оно формирует реализацию абстрактного метода, объявленного в функциональном интерфейсе.
И так, для начала, нам необходимо описать функциональный интерфейс.
import java.lang.FunctionalInterface; // функциональный интерфейс @FunctionalInterface interface MyInterface < // абстрактный метод double getPiValue(); >public class Main < public static void main( String[] args ) < // объявление ссылки на MyInterface MyInterface ref; // лямбда-выражение ref = () ->3.1415; System.out.println("Value of Pi java">Value of Pi = 3.1415
- Мы создали функциональный интерфейс MyInterface, который содержит один абстрактный метод getPiValue().
- Внутри класса Main, мы объявили ссылку на MyInterface. Обратите внимание, что мы можем объявить ссылку на интерфейс, но не можем создать его объект.
// приведет к ошибке MyInterface ref = new myInterface(); // это верно MyInterface ref;
ref = () -> 3.1415;
System.out.println("Value of Pi java">(n) -> (n % 2) == 0
В этом примере, переменная n внутри скобок является параметром, переданном в лямбда-выражение. Тело лямбды принимает параметр и проверяет его на четность.
Пример 4: использование лямбда-выражения с параметрами
@FunctionalInterface interface MyInterface < // абстрактный метод String reverse(String n); >public class Main < public static void main( String[] args ) < // объявление ссылки на MyInterface // присвоение лямбда-выражения ссылке MyInterface ref = (str) ->< String result = ""; for (int i = str.length()-1; i >= 0 ; i--) result += str.charAt(i); return result; >; // вызов метода из интерфейса System.out.println("Lambda reversed = " + ref.reverse("Lambda")); > >
Результат выполнения:
Lambda reversed = adbmaL
Параметризированный функциональный интерфейс
До этого момента, мы использовали функциональные интерфейсы, которые принимали только один тип значения. Например:
@FunctionalInterface interface MyInterface
Вышеупомянутый функциональный интерфейс принимает только String и возвращает String. Однако, мы можем сделать наш интерфейс универсальным, чтобы использовать с любым типом данных.
Пример 5: параметризированный интерфейс и лямбда-выражения
// Параметризированный интерфейс @FunctionalInterface interface GenericInterface < // параметризированный метод T func(T t); >public class Main < public static void main( String[] args ) < // Объявление ссылки на параметризированный интерфейс // который принимает String // и присвоение ей лямбды GenericInterfacereverse = (str) -> < String result = ""; for (int i = str.length()-1; i >= 0 ; i--) result += str.charAt(i); return result; >; System.out.println("Lambda reversed = " + reverse.func("Lambda")); // Объявление ссылки на параметризированный интерфейс // который принимает Integer // и присвоение ей лямбды GenericInterface factorial = (n) -> < int result = 1; for (int i = 1; i ; System.out.println("factorial of 5 java">Lambda reversed = adbmaL factorial of 5 = 120
В этом примере, мы создали параметризированный функциональный интерфейс GenericInterface, который содержит параметризированный метод func().
Затем, внутри класса Main:
- GenericInterface reverse – создает ссылку на интерфейс, который работает со String.
- GenericInterface factorial — создает ссылку на интерфейс, который работает с Integer.
Лямбда-выражения и Stream API
В JDK8 добавлен новый пакет java.util.stream, который позволяет java-разработчикам выполнять такие операции, как поиск, фильтрация, сопоставление, объединение или манипулирование коллекциями, к примеру Lists.
Например, у нас есть поток данных (в нашем случае список строк), где каждая строка содержит название страны и ее город. Теперь мы можем обработать этот поток данных и выбрать только города Непала.
Для этого мы можем использовать комбинацию Stream API и лямбда-выражений.
Пример 6: использование лямбд в Stream API
import java.util.ArrayList; import java.util.List; public class StreamMain < // объявление списка static Listplaces = new ArrayList<>(); // заполнение данными public static List getPlaces() < // добавление страны и города places.add("Nepal, Kathmandu"); places.add("Nepal, Pokhara"); places.add("India, Delhi"); places.add("USA, New York"); places.add("Africa, Nigeria"); return places; >public static void main( String[] args ) < ListmyPlaces = getPlaces(); System.out.println("Places from Nepal:"); // Фильтрация городов myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p)); > >
Результат выполнения:
Places from Nepal: NEPAL, KATHMANDU NEPAL, POKHARA
В приведенном выше примере обратите внимание на это выражение:
myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p));
Здесь мы используем такие методы, как filter(), map(), forEach() из Stream API, которые могут принимать лямбды в качестве параметра.
Также, мы можем описать собственные выражения на основе синтаксиса, описанного выше. Это позволит нам уменьшить количество строк кода.
- java 8
- lambda expressions
- stream api
Что такое лямбда-выражения в Java
Ознакомьтесь с мощными и гибкими лямбда-выражениями в Java, которые упрощают код и делают его более читабельным!
Алексей Кодов
Автор статьи
9 июня 2023 в 16:31
Лямбда-выражения являются важным нововведением в Java, начиная с версии Java 8. Они представляют собой сокращенный синтаксис для представления объектов, реализующих функциональные интерфейсы, и позволяют писать более компактный и выразительный код.
Функциональные интерфейсы
Функциональный интерфейс — это интерфейс с одним абстрактным методом. Java 8 включает также механизм default методов, которые позволяют добавлять реализацию методов в интерфейсы, но функциональный интерфейс должен содержать только один абстрактный метод. Пример функционального интерфейса:
@FunctionalInterface public interface MyFunction
Java-разработчик: новая работа через 11 месяцев
Получится, даже если у вас нет опыта в IT
Синтаксис лямбда-выражений
Лямбда-выражение представляет собой анонимную функцию, которую можно передать в качестве аргумента или присвоить переменной. Синтаксис лямбда-выражений включает в себя параметры, стрелку -> и тело выражения. Пример использования лямбда-выражения:
MyFunction square = x -> x * x; int result = square.apply(4); // Результат: 16
Преимущества лямбда-выражений
Лямбда-выражения упрощают написание кода и делают его более читабельным. Например, они могут использоваться с методами высшего порядка, такими как filter , map и reduce , для обработки коллекций данных без необходимости писать многословные анонимные классы.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); List<Integer> evenNumbers = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList());
С использованием лямбда-выражений код становится более кратким и выразительным, позволяя разработчикам сосредоточиться на решении задачи, а не на синтаксисе.
Заключение
Лямбда-выражения — это мощная и гибкая функция Java, которая упрощает написание кода и делает его более читабельным. Они играют ключевую роль в функциональном программировании на Java и являются неотъемлемой частью современного Java-разработчика.
Что такое лямбда выражение в java
Denis Gritsay Уровень 23
28 октября 2023
это : // Старый способ: List
Vitaly Demchenko Уровень 35
Лямбда-выражения в Java
Лямбда-выражения в Java — это специальные блоки с характеристиками, которые подставляются из других строк в программе. Применение метода помогает сократить объем кода, сделать текст прозрачным и ускорить компиляцию. Прочитав статью, вы узнаете, что такое λ-выражения в Java, какие задачи они решают, и почему стоит научиться их использовать.
Что такое лямбда-выражения
Название метода произошло от лямбда-исчисления, которое разработал профессор Алонзо Черч. При вычислениях он отмечал параметры греческой буквой λ. В языке Java лямбда-выражениями называют анонимные классы, у которых нет имен. Метод был добавлен в 8-ой версии синтаксиса. Анонимные классы позволяют реализовать функциональные интерфейсы и сделать программу более читаемой. Они передают блок в качестве характеристики для подстановки в сторонний блок. Функциональными интерфейсами в Java называют описание метода без тела. Данный интерфейс включает только один абстрактный метод. Одним из аргументов является готовый алгоритм. У λ-выражений есть доступ к переменным из зоны видимости. После вставки характеристик они будут возвращать значения, которые соответствуют критериям. Нельзя настроить возврат только в выбранных ветках, а в следующих отключить эту возможность.
Для чего они необходимы
При разработке софта часто возникают ситуации, когда в разных фрагментах должен выполняться повторяющийся алгоритм. Разница между блоками заключается только в типах данных, которые в них подставляются. Тогда лучше не копировать похожие блоки, а создать новый метод и вызывать его в разных местах. Предложенный метод решения проблемы подходит для передачи простых данных. Иногда в метод требуется вставлять не обычные переменные, а исполняемые блоки. Может понадобиться создать алгоритм для обработки элементов массива, которые соответствуют списку критериев. Но условия сортировки могут быть неизвестными или меняться во время работы. Решить эту задачу помогают функциональные интерфейсы.
Структура описания
Описание λ-выражения состоит из перечисления параметров и стрелки, после которой идет тело функции. Когда в левой части указан один критерий, скобки не обязательны. Если критериев 0 или больше 1, их нужно проставлять. В блоках, которые записываются одной строкой, команду return можно не указывать. Запись проводится по стандарту: (перечисление критериев) -> тело Ключевым элементом в записи является (->). Этот символ называется лямбда-оператор. Использование алгоритма для склейки двух строчек текста: interface StringConcat public String sconcat (String a, String b); > public class Example public static void main (String args[]) StringConcat s = (str1, str2) -> str1 + str2; System.out.println («Result: «+s.sconcat («Привет «, «мир»)); > >
Параметры в лямбда-выражениях
- Более двух параметров: operation = (int x, int y)->x+y;
- Без дополнительных критериев: ()-> 30 + 20;
- С единственным параметром: n-> n * n.
Пример алгоритма, который проверяет цифры на четность:
Более сложный пример: интерфейс, который переставляет буквы в слове в обратном порядке.
String reverse (String n);
public class Main
public static void main (String[] args)
MyInterface ref = (str) ->
for (int i = str. length ()-1; i >= 0; i—)
result += str. charAt (i);
System.out.println («Lambda reversed = «+ ref. reverse («Lambda»));
Результат выполнения алгоритма:
Lambda reversed = adbmaL
Типы лямбда-выражений
Принято классифицировать алгоритмы по числу строчек. Блоки разделяются на два вида:
- однострочные;
- блочные или многострочные.
Однострочный алгоритм, который выводит на экран определенный текст:
() -> System.out.println («Cats are great»);
Многострочный алгоритм, который возвращает число Пи. Для вызова числа применяется оператор return.
double pi = 3.1415;
Блочная запись позволяет выполнять несколько операций внутри одного интерфейса. Каждое из требуемых действий записывается в фигурных скобках. Команды необходимо разделять точкой с запятой. Внутри алгоритма могут быть вложенные блоки, повторяющиеся циклы, конструкции if и switch и другие элементы.
Примеры использования
В этом разделе мы с вами рассмотрим несколько простых алгоритмов, написанных на Java. Блоки с λ-выражениями не могут выполняться без привязки к функциональному интерфейсу. Чаще всего они используются для сортировки и фильтрации массивов.
Функциональные интерфейсы
Как объявить функциональный интерфейс в среде разработки:
public interface MyInterface
Предложенный интерфейс включает единственный абстрактный метод под названием getValue (). По этому критерию можно отнести его к функциональным.
Аннотация FunctionalInterface позволяет компилятору определить статус блока. Но при разработке алгоритмов разрешается ее опускать. В интерфейс можно вставить несколько лямбда-выражений для выполнения перечня операций, не связанных друг с другом.
Использование для сортировки
Разработчики часто используют метод при сортировке массивов. Он применяется в компараторах — алгоритмах для парного сравнения по выбранным критериям. Например, при сортировке чисел по возрастанию используются 3 состояния: больше, меньше или равно.
Мы с вами рассмотрим простое действие, сортировку массива с названиями цветов по последней букве слова:
List colors = Arrays. asList («Red», «Green», «Blue»);
Collections.sort (colors, (o1, o2) ->
String o1LastLetter = o1. substring (o1.length () — 1);
String o2LastLetter = o2. substring (o2.length () — 1);
return o1LastLetter. compareTo (o2LastLetter);
Использование для фильтрации
Прописать λ-выражения можно для фильтрации коллекции. Для примера рассмотрим одну более сложную задачу. Допустим, нам требуется отфильтровать коллекцию по значению (filter), изменить элементы (map) и подготовить перечень (collect).
Как правильно решить предложенную задачу:
final double currencyUsdRub = 80;
List pricesRub = Arrays. asList (25d, 50d, 60d, 12d, 45d, 89d);
List pricesUsdGreater50Rub = pricesRub. stream ()
.map (d -> d / currencyUsdRub)
Особенности работы
Рассмотрим еще несколько примеров использования лямбда-выражений. Они часто применяются для захвата переменных из разных частей программы. При написании блока можно задать список исключений из установленного правила.
Условия захвата переменных
Лямбда-выражения выполняют захват локальных переменных на уровне класса и метода. Данные для обработки подставляются из разных частей программы.
Рассмотрим пример по сложению двух чисел. Он интересен тем, что первая переменная меняется на заданное число, а вторая остается неизменной.
public class LambdaApp
static int x = 10;
static int y = 20;
public static void main (String[] args)
В результате выполнения описанного алгоритма получится число 50. Локальные переменные для расчетов объявлены на уровне классов, поэтому с ними не возникает проблем. О недостатках при работе на уровне методов мы расскажем в соответствующем разделе.
Алгоритмы для работы с исключениями
При написании алгоритмов может возникнуть необходимость сделать исключение из правила. Для этого внутри лямбда-выражения следует задать условие, при выполнении которого автоматически будет генерироваться исключение. В библиотеке Java есть заранее определенные исключения. Если рабочая ситуация не соответствует шаблонам, критерии проверки можно задать вручную.
На возникновение нестандартной ситуации указывает команда throw. Условие, от которого зависит генерация исключения, прописывается в блоке try-catch. Если заданный критерий не выполняется, процесс обрабатывается стандартным способом. При выполнении условия программа генерирует исключение в ответ на запрос.
Рассмотрим программу, которая делит одно число на другое и проверяет, равно ли второе число нулю. Если заданное условие не выполняется, программа выводит на экран результат расчетов. Если второе число представляет собой ноль, алгоритм создает исключение. Пользователь увидит результат 0.0.
Вычисления проводятся с помощью алгоритма:
double Division (double a, double b);
public class Lambda
public static void main (String[] args)
throw new ArithmeticException («Exception: divide by zero.»);
catch (ArithmeticException e)
double res = ref. Division (5, 0);
System.out.println («res = «+ res);
Что увидит пользователь, когда второе число равно нулю:
Exception: divide by zero.
Преимущества использования
Краткость и удобство кода. Чем меньше строчек требуется для выполнения алгоритма, тем лучше. Каждая строчка в программе должна быть понятной любому разработчику, который в первый раз видит файл. Требование к читаемости кода упрощает работу в компании, поскольку проекты легко передавать другим специалистам. Умение писать лаконично полезно и для самого программиста. Краткость экономит рабочее время и помогает быстрее находить и исправлять ошибки.
Отложенное выполнение (deferred execution). Разработчик может прописать шаблон в одном блоке и вызывать его неограниченное число раз в других фрагментах программы. Система отложенного выполнения позволяет:
- Запускать алгоритм в отдельном потоке.
- Повторять одну операцию несколько раз.
- Запускать алгоритм после выполнения заданного условия.
Использование функциональных интерфейсов. Реализация этих блоков избавляет разработчиков от громоздких записей и дает возможность быстро преобразовать функциональность в метод. За счет лямбды можно работать с Java как с функциональными системами программирования. Такая возможность обеспечивает принципиально новый подход к записи алгоритмов.
Недостатки использования
Сложность понимания концепции. Многие начинающие веб разработчики жалуются на то, что им трудно освоить принцип работы и научиться правильно вставлять ссылки. Решить эту и другие проблемы можно, если заниматься на онлайн-курсах, а не изучать программирование по книгам и видеоролик. Преподаватель будет взаимодействовать с вами и отвечать на вопросы так, чтобы понятно объяснить сложные моменты для начинающих
Ограничения по работе с локальными переменными иногда вызывают сложности даже у опытных веб разработчиков. В λ-выражения можно подставлять локальные переменные, объявленные в общей зоне видимости алгоритма, на уровне метода или класса. Если они выделены в пределах класса, их можно подставлять или изменять в процессе обработки массива.
Когда локальные переменные выделены на уровне метода, их можно подставлять в лямбды, но нельзя их изменять. При попытке выполнить это действие среда разработки покажет сообщение об ошибке. Появится рекомендация отметить переменную командой final, чтобы превратить ее в константу (например, final int x=50).
Не получится изменить ссылки, к которым обращаются λ-выражения, даже за пределами блока. Фактически они становятся константами, даже если специально не отмечать их указателем final.
Лямбда-выражения — это анонимные блоки, которые обращаются к данным из других фрагментов программы. Они предназначены для реализации функциональных интерфейсов. Работа с ними упрощает программу, делает ее прозрачной и легко читаемой, ускоряет компиляцию алгоритма.
На смену сложной последовательности циклов и условных операторов приходят короткие цепочки вызовов. Алгоритмы записывается в стиле функционального программирования. Для получения желаемого результата методы применяются к массивам или другим операциям.
Умение использовать λ-выражения повышает шансы устроиться на интересную и перспективную работу. Многим IT-компаниям необходимы программисты, которые умеют писать функциональные интерфейсы. Стремлением к краткости и удобству программ является одним из ведущих трендов в веб разработке. Получить необходимые для трудоустройства знания и навыки можно на онлайн курсах для Java разработчиков.