Ebben a cikkben megismerjük a Java lambda kifejezést és a lambda kifejezés használatát funkcionális interfészekkel, általános funkcionális interfészekkel és stream API-val példák segítségével.
A lambda kifejezést először a Java 8-ban vezették be. Fő célja a nyelv kifejező erejének növelése.
De mielőtt belekezdenénk a lambdákba, először meg kell értenünk a funkcionális interfészeket.
Mi az a funkcionális interfész?
Ha egy Java felület egyetlen és csak egy absztrakt módszert tartalmaz, akkor funkcionális felületnek nevezzük. Ez az egyetlen módszer határozza meg az interfész rendeltetését.
Például az Runnable
interfész a csomagból java.lang
; funkcionális interfész, mert csak egy módszert alkot, azaz run()
.
1. példa: Adjon meg egy funkcionális felületet a Java-ban
import java.lang.FunctionalInterface; @FunctionalInterface public interface MyInterface( // the single abstract method double getValue(); )
A fenti példában a MyInterface felületnek csak egy absztrakt metódusa van a getValue (). Ezért ez egy funkcionális interfész.
Itt az annotációt használtuk @FunctionalInterface
. Az annotáció arra kényszeríti a Java fordítót, hogy jelezze, hogy az interfész funkcionális interfész. Ennélfogva nem engedi meg, hogy egynél több elvont módszer legyen. Ez azonban nem kötelező.
A Java 7-ben a funkcionális interfészeket Single Abstract Methods vagy SAM típusnak tekintették. A SAM-okat általában a Névtelen osztályokkal hajtották végre a Java 7-ben.
2. példa: Névtelen osztályokkal valósítsa meg a SAM-ot a java-ban
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(); ) )
Kimenet :
Most implementáltam a Futható funkcionális interfészt.
Itt egy névtelen osztályt átadhatunk egy módszernek. Ez segít kevesebb kódot tartalmazó programok írásában a Java 7-ben. A szintaxis azonban még mindig nehéz volt, és sok extra kódsorra volt szükség.
A Java 8 kibővítette a SAM-ok erejét azzal, hogy egy lépéssel tovább lépett. Mivel tudjuk, hogy a funkcionális interfésznek csak egy módszere van, ezért nem kell definiálni a metódus nevét, amikor argumentumként adjuk át. A lambda kifejezés pontosan ezt teszi lehetővé.
Bevezetés a lambda kifejezésekbe
A lambda kifejezés lényegében egy névtelen vagy meg nem nevezett módszer. A lambda kifejezés nem hajtja végre önmagában. Ehelyett egy funkcionális interfész által meghatározott módszer megvalósítására szolgál.
Hogyan definiálható a lambda kifejezés a Java-ban?
Így definiálhatjuk a lambda kifejezést a Java-ban.
(parameter list) -> lambda body
A használt új operátor ( ->
) nyíl operátor vagy lambda operátor néven ismert. A szintaxis jelenleg nem biztos, hogy világos. Fedezzünk fel néhány példát,
Tegyük fel, hogy van egy ilyen módszerünk:
double getPiValue() ( return 3.1415; )
Ezt a módszert írhatjuk lambda kifejezéssel:
() -> 3.1415
Itt a metódusnak nincsenek paraméterei. Ezért az operátor bal oldala tartalmaz egy üres paramétert. A jobb oldal a lambda test, amely meghatározza a lambda kifejezés működését. Ebben az esetben a 3,1415 értéket adja vissza.
A lambda test típusai
Java-ban a lambda test kétféle.
1. Test egyetlen kifejezéssel
() -> System.out.println("Lambdas are great");
Ez a fajta lambda test kifejező test néven ismert.
2. Törzs, amely kódblokkból áll.
() -> ( double pi = 3.1415; return pi; );
Ez a fajta lambda test blokk testként ismert. A blokktest lehetővé teszi, hogy a lambda test több állítást is tartalmazzon. Ezek az állítások a zárójelek közé vannak zárva, és a zárójelek után pontosvesszőt kell hozzáadni.
Megjegyzés : A blokk törzséhez visszatérési utasítással rendelkezhet, ha a törzs értéket ad vissza. A törzs kifejezés azonban nem igényel visszatérési utasítást.
3. példa: Lambda kifejezés
Írjunk egy Java programot, amely a lambda kifejezés segítségével visszaadja a Pi értékét.
Mint korábban említettük, a lambda kifejezést nem hajtják végre önmagában. Inkább a funkcionális interfész által meghatározott elvont módszer megvalósítását képezi.
Tehát először meg kell határoznunk egy funkcionális interfészt.
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()); ) )
Kimenet :
Pi értéke 3,1415
A fenti példában
- Létrehoztuk a MyInterface nevű funkcionális felületet. Ez egyetlen elvont módszert tartalmaz
getPiValue()
- A Main osztály belsejében deklaráltuk a MyInterface hivatkozást. Ne feledje, hogy deklarálhatunk egy interfész referenciáját, de nem tudunk példányosítani egy interfészt. Vagyis
// it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
- Ezután lambda kifejezést rendeltünk a referenciához.
ref = () -> 3.1415;
- Végül meghívjuk a módszert
getPiValue()
a referencia felület segítségével. Mikor
System.out.println("Value of Pi = " + ref.getPiValue());
Lambda kifejezések paraméterekkel
Eddig létrehoztunk lambda kifejezéseket paraméterek nélkül. A módszerekhez hasonlóan azonban a lambda kifejezéseknek is lehetnek paramétereik. Például,
(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 onString
type of data.GenericInterface factorial
- creates a reference to the interface. The interface, in this case, operates on theInteger
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.
Saját kifejezéseinket is meghatározhatjuk a fent tanult szintaxis alapján. Ez lehetővé teszi számunkra a kódsorok drasztikus csökkentését, amint azt a fenti példában láttuk.