Expressões Lambda Java (com exemplos)

Neste artigo, aprenderemos sobre a expressão lambda Java e o uso da expressão lambda com interfaces funcionais, interface funcional genérica e API de fluxo com a ajuda de exemplos.

A expressão lambda foi introduzida pela primeira vez em Java 8. Seu principal objetivo é aumentar o poder expressivo da linguagem.

Mas, antes de entrar em lambdas, primeiro precisamos entender as interfaces funcionais.

O que é interface funcional?

Se uma interface Java contém um e apenas um método abstrato, ela é denominada interface funcional. Este apenas um método especifica o propósito pretendido da interface.

Por exemplo, a Runnableinterface do pacote java.lang; é uma interface funcional porque constitui apenas um método, ou seja run().

Exemplo 1: Definir uma interface funcional em java

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

No exemplo acima, a interface MyInterface possui apenas um método abstrato getValue (). Portanto, é uma interface funcional.

Aqui, usamos a anotação @FunctionalInterface. A anotação força o compilador Java a indicar que a interface é funcional. Portanto, não permite ter mais de um método abstrato. No entanto, não é obrigatório.

No Java 7, as interfaces funcionais eram consideradas métodos abstratos únicos ou tipo SAM . SAMs eram comumente implementados com Anonymous Classes em Java 7.

Exemplo 2: Implementar SAM com classes anônimas em 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(); ) )

Produto :

 Acabei de implementar a Interface Funcional Executável.

Aqui, podemos passar uma classe anônima para um método. Isso ajuda a escrever programas com menos códigos em Java 7. No entanto, a sintaxe ainda era difícil e muitas linhas extras de código eram necessárias.

Java 8 estendeu o poder de um SAMs dando um passo adiante. Como sabemos que uma interface funcional possui apenas um método, não deve haver necessidade de definir o nome desse método ao passá-lo como um argumento. A expressão lambda nos permite fazer exatamente isso.

Introdução às expressões lambda

A expressão lambda é, essencialmente, um método anônimo ou sem nome. A expressão lambda não é executada por conta própria. Em vez disso, é usado para implementar um método definido por uma interface funcional.

Como definir a expressão lambda em Java?

Aqui está como podemos definir a expressão lambda em Java.

 (parameter list) -> lambda body

O operador new ( ->) usado é conhecido como operador de seta ou operador lambda. A sintaxe pode não estar clara no momento. Vamos explorar alguns exemplos,

Suponha que temos um método como este:

 double getPiValue() ( return 3.1415; )

Podemos escrever este método usando a expressão lambda como:

 () -> 3.1415

Aqui, o método não possui parâmetros. Portanto, o lado esquerdo do operador inclui um parâmetro vazio. O lado direito é o corpo lambda que especifica a ação da expressão lambda. Nesse caso, ele retorna o valor 3,1415.

Tipos de corpo lambda

Em Java, o corpo lambda é de dois tipos.

1. Um corpo com uma única expressão

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

Esse tipo de corpo lambda é conhecido como corpo de expressão.

2. Um corpo que consiste em um bloco de código.

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

Esse tipo de corpo lambda é conhecido como corpo de bloco. O corpo do bloco permite que o corpo lambda inclua várias instruções. Essas instruções são colocadas entre colchetes e você deve adicionar um ponto-e-vírgula após as chaves.

Nota : Para o corpo do bloco, você pode ter uma instrução de retorno se o corpo retornar um valor. No entanto, o corpo da expressão não requer uma instrução de retorno.

Exemplo 3: Expressão Lambda

Vamos escrever um programa Java que retorna o valor de Pi usando a expressão lambda.

Conforme mencionado anteriormente, uma expressão lambda não é executada por conta própria. Em vez disso, ele forma a implementação do método abstrato definido pela interface funcional.

Portanto, precisamos primeiro definir uma interface funcional.

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

Produto :

 Valor de Pi = 3,1415

No exemplo acima,

  • Criamos uma interface funcional chamada MyInterface. Ele contém um único método abstrato chamadogetPiValue()
  • Dentro da classe Main, declaramos uma referência a MyInterface. Observe que podemos declarar uma referência de uma interface, mas não podemos instanciar uma interface. Isso é,
     // it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
  • Em seguida, atribuímos uma expressão lambda à referência.
     ref = () -> 3.1415;
  • Finalmente, chamamos o método getPiValue()usando a interface de referência. Quando
     System.out.println("Value of Pi = " + ref.getPiValue());

Expressões Lambda com parâmetros

Até agora, criamos expressões lambda sem nenhum parâmetro. No entanto, semelhante aos métodos, as expressões lambda também podem ter parâmetros. Por exemplo,

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

Também podemos definir nossas próprias expressões com base na sintaxe que aprendemos acima. Isso nos permite reduzir drasticamente as linhas de código, como vimos no exemplo acima.

Artigos interessantes...