Expressions Java Lambda (avec exemples)

Dans cet article, nous allons en apprendre davantage sur l'expression Java lambda et l'utilisation de l'expression lambda avec des interfaces fonctionnelles, une interface fonctionnelle générique et une API de flux à l'aide d'exemples.

L'expression lambda a été introduite pour la première fois en Java 8. Son objectif principal est d'augmenter la puissance expressive du langage.

Mais, avant d'entrer dans lambdas, nous devons d'abord comprendre les interfaces fonctionnelles.

Qu'est-ce que l'interface fonctionnelle?

Si une interface Java contient une et une seule méthode abstraite, elle est appelée interface fonctionnelle. Cette seule méthode spécifie le but visé de l'interface.

Par exemple, l' Runnableinterface du package java.lang; est une interface fonctionnelle car elle ne constitue qu'une seule méthode ie run().

Exemple 1: définir une interface fonctionnelle en java

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

Dans l'exemple ci-dessus, l'interface MyInterface n'a qu'une seule méthode abstraite getValue (). C'est donc une interface fonctionnelle.

Ici, nous avons utilisé l'annotation @FunctionalInterface. L'annotation force le compilateur Java à indiquer que l'interface est une interface fonctionnelle. Par conséquent, ne permet pas d'avoir plus d'une méthode abstraite. Cependant, ce n'est pas obligatoire.

Dans Java 7, les interfaces fonctionnelles étaient considérées comme des méthodes abstraites uniques ou de type SAM . Les SAM étaient généralement implémentés avec des classes anonymes dans Java 7.

Exemple 2: implémenter SAM avec des classes anonymes en 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(); ) )

Sortie :

 Je viens de mettre en œuvre l'interface fonctionnelle exécutable.

Ici, nous pouvons passer une classe anonyme à une méthode. Cela aide à écrire des programmes avec moins de codes en Java 7. Cependant, la syntaxe était encore difficile et beaucoup de lignes de code supplémentaires étaient nécessaires.

Java 8 a étendu la puissance d'un SAM en allant plus loin. Puisque nous savons qu'une interface fonctionnelle n'a qu'une seule méthode, il ne devrait pas être nécessaire de définir le nom de cette méthode lorsque vous la passez en argument. L'expression Lambda nous permet de faire exactement cela.

Introduction aux expressions lambda

L'expression Lambda est, essentiellement, une méthode anonyme ou sans nom. L'expression lambda ne s'exécute pas d'elle-même. Au lieu de cela, il est utilisé pour implémenter une méthode définie par une interface fonctionnelle.

Comment définir une expression lambda en Java?

Voici comment nous pouvons définir l'expression lambda en Java.

 (parameter list) -> lambda body

Le nouvel opérateur ( ->) utilisé est appelé opérateur de flèche ou opérateur lambda. La syntaxe peut ne pas être claire pour le moment. Explorons quelques exemples,

Supposons que nous ayons une méthode comme celle-ci:

 double getPiValue() ( return 3.1415; )

Nous pouvons écrire cette méthode en utilisant l'expression lambda comme suit:

 () -> 3.1415

Ici, la méthode n'a aucun paramètre. Par conséquent, le côté gauche de l'opérateur comprend un paramètre vide. Le côté droit est le corps lambda qui spécifie l'action de l'expression lambda. Dans ce cas, il renvoie la valeur 3,1415.

Types de corps Lambda

En Java, le corps lambda est de deux types.

1. Un corps avec une seule expression

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

Ce type de corps lambda est appelé corps d'expression.

2. Un corps constitué d'un bloc de code.

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

Ce type de corps lambda est appelé corps de bloc. Le corps du bloc permet au corps lambda d'inclure plusieurs instructions. Ces instructions sont placées entre les accolades et vous devez ajouter un point-virgule après les accolades.

Remarque : Pour le corps du bloc, vous pouvez avoir une instruction return si le corps renvoie une valeur. Cependant, le corps de l'expression ne nécessite pas d'instruction return.

Exemple 3: Expression Lambda

Écrivons un programme Java qui renvoie la valeur de Pi à l'aide de l'expression lambda.

Comme mentionné précédemment, une expression lambda n'est pas exécutée seule. Il forme plutôt l'implémentation de la méthode abstraite définie par l'interface fonctionnelle.

Nous devons donc d'abord définir une interface fonctionnelle.

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

Sortie :

 Valeur de Pi = 3,1415

Dans l'exemple ci-dessus,

  • Nous avons créé une interface fonctionnelle nommée MyInterface. Il contient une seule méthode abstraite nomméegetPiValue()
  • Dans la classe Main, nous avons déclaré une référence à MyInterface. Notez que nous pouvons déclarer une référence d'interface mais nous ne pouvons pas instancier une interface. C'est,
     // it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
  • Nous avons ensuite attribué une expression lambda à la référence.
     ref = () -> 3.1415;
  • Enfin, nous appelons la méthode en getPiValue()utilisant l'interface de référence. Quand
     System.out.println("Value of Pi = " + ref.getPiValue());

Expressions Lambda avec paramètres

Jusqu'à présent, nous avons créé des expressions lambda sans aucun paramètre. Cependant, à l'instar des méthodes, les expressions lambda peuvent également avoir des paramètres. Par exemple,

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

Nous pouvons également définir nos propres expressions en fonction de la syntaxe que nous avons apprise ci-dessus. Cela nous permet de réduire considérablement les lignes de code comme nous l'avons vu dans l'exemple ci-dessus.

Articles intéressants...