Wyrażenia Lambda w języku Java (z przykładami)

W tym artykule dowiemy się o wyrażeniu lambda w Javie i zastosowaniu wyrażenia lambda z interfejsami funkcjonalnymi, ogólnym interfejsem funkcjonalnym i strumieniowym API z pomocą przykładów.

Wyrażenie lambda zostało wprowadzone po raz pierwszy w Javie 8. Jego głównym celem jest zwiększenie możliwości ekspresji języka.

Ale zanim przejdziemy do lambd, musimy najpierw zrozumieć funkcjonalne interfejsy.

Co to jest interfejs funkcjonalny?

Jeśli interfejs Java zawiera jedną i tylko jedną metodę abstrakcyjną, określa się go mianem interfejsu funkcjonalnego. Ta tylko jedna metoda określa przeznaczenie interfejsu.

Na przykład Runnableinterfejs z pakietu java.lang; jest interfejsem funkcjonalnym, ponieważ stanowi tylko jedną metodę, tj run().

Przykład 1: Zdefiniuj interfejs funkcjonalny w java

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

W powyższym przykładzie interfejs MyInterface ma tylko jedną abstrakcyjną metodę getValue (). Dlatego jest to funkcjonalny interfejs.

Tutaj użyliśmy adnotacji @FunctionalInterface. Adnotacja wymusza na kompilatorze Java wskazanie, że interfejs jest interfejsem funkcjonalnym. Dlatego nie pozwala na posiadanie więcej niż jednej metody abstrakcyjnej. Jednak nie jest to obowiązkowe.

W Javie 7 funkcjonalne interfejsy były traktowane jako pojedyncze metody abstrakcyjne lub typu SAM . SAM były często implementowane z klasami anonimowymi w Javie 7.

Przykład 2: Zaimplementuj SAM z anonimowymi klasami w 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(); ) )

Wyjście :

 Właśnie zaimplementowałem Runnable Functional Interface.

Tutaj możemy przekazać anonimową klasę do metody. Pomaga to w pisaniu programów z mniejszą liczbą kodów w Javie 7. Jednak składnia była nadal trudna i wymagało wielu dodatkowych linii kodu.

Java 8 rozszerzyła możliwości SAM-ów, idąc o krok dalej. Ponieważ wiemy, że interfejs funkcjonalny ma tylko jedną metodę, nie powinno być potrzeby definiowania nazwy tej metody podczas przekazywania jej jako argumentu. Wyrażenie lambda pozwala nam to zrobić.

Wprowadzenie do wyrażeń lambda

Wyrażenie lambda jest zasadniczo metodą anonimową lub nienazwaną. Wyrażenie lambda nie jest wykonywane samodzielnie. Zamiast tego służy do implementacji metody zdefiniowanej przez interfejs funkcjonalny.

Jak zdefiniować wyrażenie lambda w Javie?

Oto jak możemy zdefiniować wyrażenie lambda w Javie.

 (parameter list) -> lambda body

->Używany nowy operator ( ) jest nazywany operatorem strzałki lub operatorem lambda. Składnia może nie być w tej chwili jasna. Przeanalizujmy kilka przykładów,

Załóżmy, że mamy taką metodę:

 double getPiValue() ( return 3.1415; )

Możemy napisać tę metodę używając wyrażenia lambda jako:

 () -> 3.1415

Tutaj metoda nie ma żadnych parametrów. Stąd lewa strona operatora zawiera pusty parametr. Prawa strona to treść lambda, która określa działanie wyrażenia lambda. W tym przypadku zwraca wartość 3,1415.

Rodzaje Lambda Body

W Javie ciało lambda jest dwojakiego rodzaju.

1. Ciało z jednym wyrazem

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

Ten typ treści lambda jest nazywany treścią wyrażenia.

2. Treść składająca się z bloku kodu.

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

Ten typ ciała lambda jest znany jako bryła blokowa. Treść bloku umożliwia treści lambda zawarcie wielu instrukcji. Te instrukcje są zawarte w nawiasach klamrowych i po nawiasach należy dodać średnik.

Uwaga : W przypadku treści bloku możesz mieć instrukcję return, jeśli treść zwraca wartość. Jednak treść wyrażenia nie wymaga instrukcji return.

Przykład 3: Wyrażenie lambda

Napiszmy program w języku Java, który zwraca wartość liczby Pi za pomocą wyrażenia lambda.

Jak wspomniano wcześniej, wyrażenie lambda nie jest wykonywane samodzielnie. Stanowi raczej implementację metody abstrakcyjnej zdefiniowanej przez interfejs funkcjonalny.

Dlatego musimy najpierw zdefiniować interfejs funkcjonalny.

 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()); ) )

Wyjście :

 Wartość Pi = 3,1415

W powyższym przykładzie

  • Stworzyliśmy funkcjonalny interfejs o nazwie MyInterface. Zawiera jedną abstrakcyjną metodę o nazwiegetPiValue()
  • W klasie Main zadeklarowaliśmy odniesienie do MyInterface. Zauważ, że możemy zadeklarować odniesienie do interfejsu, ale nie możemy utworzyć instancji interfejsu. To jest,
     // it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
  • Następnie przypisaliśmy wyrażenie lambda do odwołania.
     ref = () -> 3.1415;
  • Na koniec wywołujemy metodę getPiValue()za pomocą interfejsu referencyjnego. Kiedy
     System.out.println("Value of Pi = " + ref.getPiValue());

Wyrażenia lambda z parametrami

Do tej pory stworzyliśmy wyrażenia lambda bez żadnych parametrów. Jednak podobnie jak metody, wyrażenia lambda mogą również mieć parametry. Na przykład,

 (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.

Możemy również zdefiniować własne wyrażenia w oparciu o składnię, której nauczyliśmy się powyżej. Pozwala nam to drastycznie zredukować liczbę wierszy kodu, jak widzieliśmy w powyższym przykładzie.

Interesujące artykuły...