Výrazy Java Lambda (s příklady)

V tomto článku se na příkladech seznámíme s výrazem lambda Java a použitím výrazu lambda s funkčními rozhraními, obecným funkčním rozhraním a stream API.

Výraz lambda byl poprvé představen v prostředí Java 8. Jeho hlavním cílem bylo zvýšit expresivní sílu jazyka.

Ale než se pustíme do lambdas, musíme nejprve porozumět funkčním rozhraním.

Co je funkční rozhraní?

Pokud rozhraní Java obsahuje jednu a pouze jednu abstraktní metodu, pak se nazývá funkční rozhraní. Tato pouze jedna metoda určuje zamýšlený účel rozhraní.

Například Runnablerozhraní z balíčku java.lang; je funkční rozhraní, protože představuje pouze jednu metodu, tj run().

Příklad 1: Definování funkčního rozhraní v Javě

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

Ve výše uvedeném příkladu má rozhraní MyInterface pouze jednu abstraktní metodu getValue (). Jedná se tedy o funkční rozhraní.

Zde jsme použili anotaci @FunctionalInterface. Anotace vynutí kompilátor Java k označení, že rozhraní je funkční rozhraní. Proto neumožňuje mít více než jednu abstraktní metodu. Není to však povinné.

V prostředí Java 7 byla funkční rozhraní považována za Single Abstract Methods nebo SAM type. SAM byly běžně implementovány s anonymními třídami v Javě 7.

Příklad 2: Implementace SAM s anonymními třídami v Javě

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

Výstup :

 Právě jsem implementoval funkční rozhraní Runnable.

Zde můžeme předat anonymní třídu metodě. To pomáhá psát programy s menším počtem kódů v prostředí Java 7. Syntaxe však byla stále obtížná a bylo zapotřebí mnoho dalších řádků kódu.

Java 8 rozšířila výkon SAM o krok dále. Protože víme, že funkční rozhraní má pouze jednu metodu, nemělo by být nutné při předávání jako argument definovat název této metody. Lambda výraz nám umožňuje přesně to udělat.

Úvod do výrazů lambda

Lambda výraz je v zásadě anonymní nebo nepojmenovaná metoda. Výraz lambda se sám neprovádí. Místo toho se používá k implementaci metody definované funkčním rozhraním.

Jak definovat výraz lambda v Javě?

Zde je způsob, jak můžeme definovat výraz lambda v Javě.

 (parameter list) -> lambda body

Nový ->použitý operator ( ) je známý jako operátor se šipkami nebo lambda operátor. Syntaxe nemusí být v tuto chvíli jasná. Prozkoumejme několik příkladů,

Předpokládejme, že máme takovou metodu:

 double getPiValue() ( return 3.1415; )

Tuto metodu můžeme napsat pomocí výrazu lambda jako:

 () -> 3.1415

Zde metoda nemá žádné parametry. Levá strana operátoru tedy obsahuje prázdný parametr. Pravá strana je tělo lambda, které určuje akci výrazu lambda. V tomto případě vrátí hodnotu 3.1415.

Druhy lambda těla

V Javě je tělo lambda dvou typů.

1. Tělo s jediným výrazem

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

Tento typ těla lambda je známý jako tělo výrazu.

2. Tělo, které se skládá z bloku kódu.

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

Tento typ lambda těla je známý jako blokové tělo. Tělo bloku umožňuje, aby tělo lambda obsahovalo více příkazů. Tyto příkazy jsou uzavřeny uvnitř složených závorek a za složené závorky musíte přidat středník.

Poznámka : Pro tělo bloku můžete mít příkaz return, pokud tělo vrátí hodnotu. Tělo výrazu však nevyžaduje návratový příkaz.

Příklad 3: Lambda výraz

Napíšeme program Java, který vrátí hodnotu Pi pomocí výrazu lambda.

Jak již bylo zmíněno dříve, výraz lambda se sám neprovádí. Spíše tvoří implementaci abstraktní metody definované funkčním rozhraním.

Nejprve tedy musíme definovat funkční rozhraní.

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

Výstup :

 Hodnota Pi = 3,1415

Ve výše uvedeném příkladu

  • Vytvořili jsme funkční rozhraní s názvem MyInterface. Obsahuje jedinou abstraktní metodu s názvemgetPiValue()
  • Uvnitř třídy Main jsme deklarovali odkaz na MyInterface. Všimněte si, že můžeme deklarovat odkaz na rozhraní, ale nemůžeme vytvořit instanci rozhraní. To znamená,
     // it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
  • Poté jsme referenci přiřadili výraz lambda.
     ref = () -> 3.1415;
  • Nakonec metodu zavoláme getPiValue()pomocí referenčního rozhraní. Když
     System.out.println("Value of Pi = " + ref.getPiValue());

Lambda výrazy s parametry

Dosud jsme vytvořili lambda výrazy bez jakýchkoli parametrů. Podobně jako metody však výrazy lambda mohou mít také parametry. Například,

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

Můžeme také definovat vlastní výrazy na základě syntaxe, kterou jsme se naučili výše. To nám umožňuje drasticky snížit řádky kódu, jak jsme viděli ve výše uvedeném příkladu.

Zajímavé články...