Лямбда-выражения Java (с примерами)

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

Лямбда-выражение было впервые представлено в Java 8. Его основная цель - увеличить выразительную силу языка.

Но, прежде чем переходить к лямбдам, нам сначала нужно понять функциональные интерфейсы.

Что такое функциональный интерфейс?

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

Например, Runnableинтерфейс из пакета java.lang; представляет собой функциональный интерфейс , поскольку он представляет собой лишь один метод ИЭ run().

Пример 1: определение функционального интерфейса в java

 import java.lang.FunctionalInterface; @FunctionalInterface public interface MyInterface( // the single abstract method double getValue(); )

В приведенном выше примере интерфейс MyInterface имеет только один абстрактный метод getValue (). Следовательно, это функциональный интерфейс.

Здесь мы использовали аннотацию @FunctionalInterface. Аннотация заставляет компилятор Java указывать, что интерфейс является функциональным интерфейсом. Следовательно, не позволяет иметь более одного абстрактного метода. Однако это не обязательно.

В Java 7 функциональные интерфейсы рассматривались как отдельные абстрактные методы или тип SAM . SAM обычно реализовывались с анонимными классами в Java 7.

Пример 2: Реализация SAM с анонимными классами в java

 public class FunctionInterfaceTest ( public static void main(String() args) ( // anonymous class new Thread(new Runnable() ( @Override public void run() ( System.out.println("I just implemented the Runnable Functional Interface."); ) )).start(); ) )

Выход :

 Я только что реализовал функциональный интерфейс Runnable.

Здесь мы можем передать анонимный класс методу. Это помогает писать программы с меньшим количеством кода на Java 7. Однако синтаксис все еще был сложным, и требовалось много дополнительных строк кода.

Java 8 расширила возможности SAM, сделав шаг вперед. Поскольку мы знаем, что функциональный интерфейс имеет только один метод, нет необходимости определять имя этого метода при передаче его в качестве аргумента. Лямбда-выражение позволяет нам делать именно это.

Введение в лямбда-выражения

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

Как определить лямбда-выражение в Java?

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

 (parameter list) -> lambda body

Используемый оператор new ( ->) известен как оператор стрелки или лямбда-оператор. На данный момент синтаксис может быть неясным. Давайте рассмотрим несколько примеров,

Предположим, у нас есть такой метод:

 double getPiValue() ( return 3.1415; )

Мы можем написать этот метод, используя лямбда-выражение, как:

 () -> 3.1415

Здесь метод не имеет параметров. Следовательно, левая часть оператора включает пустой параметр. Правая сторона - это тело лямбда, которое определяет действие лямбда-выражения. В этом случае он возвращает значение 3,1415.

Типы лямбда-тела

В Java лямбда-тело бывает двух типов.

1. Тело с единственным выражением лица.

 () -> System.out.println("Lambdas are great");

Этот тип тела лямбда известен как тело выражения.

2. Тело, состоящее из блока кода.

 () -> ( double pi = 3.1415; return pi; );

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

Примечание : для тела блока вы можете иметь оператор return, если тело возвращает значение. Однако тело выражения не требует оператора возврата.

Пример 3: лямбда-выражение

Напишем программу на Java, которая возвращает значение Пи с помощью лямбда-выражения.

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

Итак, нам нужно сначала определить функциональный интерфейс.

 import java.lang.FunctionalInterface; // this is functional interface @FunctionalInterface interface MyInterface( // abstract method double getPiValue(); ) public class Main ( public static void main( String() args ) ( // declare a reference to MyInterface MyInterface ref; // lambda expression ref = () -> 3.1415; System.out.println("Value of Pi = " + ref.getPiValue()); ) )

Выход :

 Значение Pi = 3,1415

В приведенном выше примере

  • Мы создали функциональный интерфейс под названием MyInterface. Он содержит единственный абстрактный метод с именемgetPiValue()
  • Внутри класса Main мы объявили ссылку на MyInterface. Обратите внимание, что мы можем объявить ссылку на интерфейс, но не можем создать экземпляр интерфейса. Это,
     // it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
  • Затем мы присвоили ссылке лямбда-выражение.
     ref = () -> 3.1415;
  • Наконец, мы вызываем метод, getPiValue()используя ссылочный интерфейс. Когда
     System.out.println("Value of Pi = " + ref.getPiValue());

Лямбда-выражения с параметрами

До сих пор мы создавали лямбда-выражения без каких-либо параметров. Однако, как и методы, лямбда-выражения также могут иметь параметры. Например,

 (n) -> (n%2)==0

Here, the variable n inside the parenthesis is a parameter passed to the lambda expression. The lambda body takes the parameter and checks if it is even or odd.

Example 4: Using lambda expression with parameters

 @FunctionalInterface interface MyInterface ( // abstract method String reverse(String n); ) public class Main ( public static void main( String() args ) ( // declare a reference to MyInterface // assign a lambda expression to the reference MyInterface ref = (str) -> ( String result = ""; for (int i = str.length()-1; i>= 0 ; i--) result += str.charAt(i); return result; ); // call the method of the interface System.out.println("Lambda reversed = " + ref.reverse("Lambda")); ) )

Output:

 Lambda reversed = adbmaL

Generic Functional Interface

Till now we have used the functional interface that accepts only one type of value. For example,

 @FunctionalInterface interface MyInterface ( String reverseString(String n); )

The above functional interface only accepts String and returns String. However, we can make the functional interface generic, so that any data type is accepted. If you are not sure about generics, visit Java Generics.

Example 5: Generic Functional Interface and Lambda Expressions

 // GenericInterface.java @FunctionalInterface interface GenericInterface ( // generic method T func(T t); ) // GenericLambda.java public class Main ( public static void main( String() args ) ( // declare a reference to GenericInterface // the GenericInterface operates on String data // assign a lambda expression to it GenericInterface reverse = (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")); // declare another reference to GenericInterface // the GenericInterface operates on Integer data // assign a lambda expression to it GenericInterface factorial = (n) -> ( int result = 1; for (int i = 1; i <= n; i++) result = i * result; return result; ); System.out.println("factorial of 5 = " + factorial.func(5)); ) )

Output:

 Lambda reversed = adbmaL factorial of 5 = 120

In the above example, we have created a generic functional interface named GenericInterface. It contains a generic method named func().

Here, inside the Main class,

  • GenericInterface reverse - creates a reference to the interface. The interface now operates on String type of data.
  • GenericInterface factorial - creates a reference to the interface. The interface, in this case, operates on the Integer type of data.

Lambda Expression and Stream API

The new java.util.stream package has been added to JDK8 which allows java developers to perform operations like search, filter, map, reduce, or manipulate collections like Lists.

For example, we have a stream of data (in our case a List of String) where each string is a combination of country name and place of the country. Now, we can process this stream of data and retrieve only the places from Nepal.

For this, we can perform bulk operations in the stream by the combination of Stream API and Lambda expression.

Example 6: Demonstration of using lambdas with the Stream API

 import java.util.ArrayList; import java.util.List; public class StreamMain ( // create an object of list using ArrayList static List places = new ArrayList(); // preparing our data public static List getPlaces()( // add places and country to the list 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 ) ( List myPlaces = getPlaces(); System.out.println("Places from Nepal:"); // Filter places from Nepal myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p)); ) )

Output:

 Places from Nepal: NEPAL, KATHMANDU NEPAL, POKHARA

In the above example, notice the statement,

 myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p));

Here, we are using the methods like filter(), map() and forEach() of the Stream API. These methods can take a lambda expression as input.

Мы также можем определять наши собственные выражения на основе синтаксиса, который мы изучили выше. Это позволяет нам значительно сократить количество строк кода, как мы видели в приведенном выше примере.

Интересные статьи...