- Lambda-выражения в Java
- Введение
- Что же такое функциональный интерфейс?
- Введение в лямбда-выражения
- Как записать лямбда-выражение в Java?
- Типы лямбда-выражений
- Параметризированный функциональный интерфейс
- Лямбда-выражения и Stream API
- Как начать работать с Лямбда-выражениями в Java
- Краткое введение
- Функциональный интерфейс
- Оператор Стрелка
- Блок Лямбда-выражений
- Функциональные интерфейсы generic
- Использование Лямбда-выражений в качестве аргументов
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
Здесь мы использовали новый оператор (->) — лямбда-оператор. Возможно, синтаксис кажется немного сложным. Давайте разберем пару примеров.
Предположим, у нас есть такой метод:
Мы можем записать его, используя лямбда, как:
Этот метод не имеет никаких параметров. Следовательно, левая часть выражения содержит пустые скобки. Правая сторона – тело лямбда-выражения, которое определяет его действие. В нашем случае, возвращается значение 3.1415.
Типы лямбда-выражений
В Java, тело лямбды может быть двух типов.
() -> 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;
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")); > >
Параметризированный функциональный интерфейс
До этого момента, мы использовали функциональные интерфейсы, которые принимали только один тип значения. Например:
@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().
- 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
Привет, Хабр! Представляю вашему вниманию перевод статьи «How to start working with Lambda Expressions in Java» автора Luis Santiago.
До того как Лямбда-выражения были добавлены в JDK 8, я использовал их в таких языках как C# и С++. Когда они были добавлены в Java я стал изучать их подробнее.
С добавлением Лямбда-выражений добавились элементы синтаксиса, которые увеличивают «выразительную силу» Java. В этой статье я хочу сосредоточиться на основополагающих концепциях, с которыми вам необходимо познакомиться, чтобы начать использовать Лямбда-выражения.
Краткое введение
Лямбда-выражения используют преимущества параллельных процессов в многоядерных средах, что видно при поддержке операций с конвейерами данных в Stream API.
Это анонимные методы (методы без имени), используемые для реализации метода, определенного функциональным интерфейсом. Важно знать, что такое функциональный интерфейс, прежде чем вы начнете использовать Лямбда-выражения.
Функциональный интерфейс
Функциональный интерфейс — это интерфейс, содержащий один и только один абстрактный метод.
Если вы посмотрите на определение стандартного интерфейса Runnable, то вы заметите как он попадает в определение функционального интерфейса, поскольку он определяет только один метод: run().
В приведенном ниже примере кода метод computeName является абстрактным и единственным методом в интерфейсе MyName , что делает его функциональным интерфейсом.
Оператор Стрелка
Лямбда-выражения вводят новый оператор стрелка -> в Java. Он разделяет лямбда-выражение на 2 части:
В левой части задаются параметры, необходимые для выражения. Эта часть может быть пустой если не требуется никаких параметров.
Правая сторона — это тело выражения, которое определяет его действия.
Теперь, используя функциональные выражения и оператор стрелки можно составить просто лямбда-выражение:
interface NumericTest < boolean computeTest(int n); >public static void main(String args[]) < NumericTest isEven = (n) ->(n % 2) == 0; NumericTest isNegative = (n) -> (n < 0); // Output: false System.out.println(isEven.computeTest(5)); // Output: true System.out.println(isNegative.computeTest(-5)); >
interface MyGreeting < String processName(String str); >public static void main(String args[]) < MyGreeting morningGreeting = (str) ->"Good Morning " + str + "!"; MyGreeting eveningGreeting = (str) -> "Good Evening " + str + "!"; // Output: Good Morning Luis! System.out.println(morningGreeting.processName("Luis")); // Output: Good Evening Jessica! System.out.println(eveningGreeting.processName("Jessica")); >
Переменные morningGreeting и eveningGreeting в строках 6 и 7 соответственно в примере выше создают ссылку на интерфейс MyGreeting и определяют 2 выражения приветствия.
При написании лямбда-выражения можно явно указать тип параметра, как это делается в примере ниже:
MyGreeting morningGreeting = (String str) -> "Good Morning " + str + "!"; MyGreeting eveningGreeting = (String str) -> "Good Evening " + str + "!";
Блок Лямбда-выражений
До сих пор я рассматривал одиночные лямбда-выражения. Существует еще один тип выражения, когда справа от оператора стрелки находится не одно простое выражение и так называемый блок лямбда:
interface MyString < String myStringFunction(String str); >public static void main (String args[]) < // Block lambda to reverse string MyString reverseStr = (str) ->< String result = ""; for(int i = str.length()-1; i >= 0; i--) result += str.charAt(i); return result; >; // Output: omeD adbmaL System.out.println(reverseStr.myStringFunction("Lambda Demo")); >
Функциональные интерфейсы generic
Лямбда-выражения не могут быть generic, но функциональный интерфейс, связанный с выражением, может. Можно написать один общий интерфейс и возвращать различные типы данных, например:
interface MyGeneric < T compute(T t); >public static void main(String args[]) < // String version of MyGenericInteface MyGenericreverse = (str) -> < String result = ""; for(int i = str.length()-1; i >= 0; i--) result += str.charAt(i); return result; >; // Integer version of MyGeneric MyGeneric factorial = (Integer n) -> < int result = 1; for(int i=1; i ; // Output: omeD adbmaL System.out.println(reverse.compute("Lambda Demo")); // Output: 120 System.out.println(factorial.compute(5)); >
Использование Лямбда-выражений в качестве аргументов
Одно распространенное использование лямбда — передача их в качестве аргументов.
Вы можете передавать исполняемый код аргументам методов в качестве параметров. Для этого просто убедитесь, что тип функционального интерфейса совместим с требуемым параметром.
interface MyString < String myStringFunction(String str); >public static String reverseStr(MyString reverse, String str) < return reverse.myStringFunction(str); >public static void main (String args[]) < // Block lambda to reverse string MyString reverse = (str) ->< String result = ""; for(int i = str.length()-1; i >= 0; i--) result += str.charAt(i); return result; >; // Output: omeD adbmaL System.out.println(reverseStr(reverse, "Lambda Demo")); >
Эти концепции дадут вам хорошую основу для начала работы с лямбда-выражениями. Взгляните на свой код и посмотрите, где вы можете их использовать.