Вирази лямбда-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(); ) )

Вихід :

 Я щойно реалізував функціональний інтерфейс, що працює.

Тут ми можемо передати анонімний клас методу. Це допомагає писати програми з меншою кількістю кодів на Java 7. Однак синтаксис все ще був складним, і потрібно було додаткові рядки коду.

Java 8 розширила потужність ЗРК, зробивши крок далі. Оскільки ми знаємо, що функціональний інтерфейс має лише один метод, не повинно бути необхідності визначати ім'я цього методу, передаючи його як аргумент. Лямбда-вираз дозволяє нам робити саме це.

Вступ до лямбда-виразів

Вираз Лямбда - це, по суті, анонімний або неназваний метод. Лямбда-вираз не виконується самостійно. Натомість він використовується для реалізації методу, визначеного функціональним інтерфейсом.

Як визначити лямбда-вираз у Java?

Ось як ми можемо визначити лямбда-вираз у Java.

 (parameter list) -> lambda body

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

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

 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, якщо тіло повертає значення. Однак тіло виразу не вимагає оператора return.

Приклад 3: Лямбда-вираз

Давайте напишемо програму Java, яка повертає значення Pi за допомогою лямбда-виразу.

Як зазначалося раніше, лямбда-вираз не виконується самостійно. Швидше, він формує реалізацію абстрактного методу, визначеного функціональним інтерфейсом.

Отже, спочатку нам потрібно визначити функціональний інтерфейс.

 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.

Ми також можемо визначити наші власні вирази на основі синтаксису, який ми вивчили вище. Це дозволяє нам різко скоротити рядки коду, як ми бачили у наведеному вище прикладі.

Цікаві статті...