Expressões Java Lambda
Expressões Java Lambda
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 no 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 único método especifica o propósito pretendido da interface.
Por exemplo, o
Runnable
interface 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 é uma interface funcional. Portanto, não permite ter mais de um método abstrato. No entanto, não é obrigatório embora. No Java 7, as interfaces funcionais eram consideradas como Single Abstract Methods ou SAM modelo. SAMs eram comumente implementados com classes anônimas 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();
}
}
Saída :
I just implemented the Runnable Functional Interface.
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.
O 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 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 sozinha. 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 novo operador (
->
) 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 nenhum parâmetro. 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, 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");
Este 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;
};
Este 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 chaves e você deve adicionar um ponto e vírgula após as chaves.
Observação :Para o corpo do bloco, você pode ter uma instrução return 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 retorne o valor de Pi usando a expressão lambda.
Como mencionado anteriormente, uma expressão lambda não é executada por conta própria. Em vez disso, forma a implementação do método abstrato definido pela interface funcional.
Então, precisamos definir uma interface funcional primeiro.
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());
}
}
Saída :
Value of Pi = 3.1415
No exemplo acima,
- Criamos uma interface funcional chamada MyInterface . Ele contém um único método abstrato chamado
getPiValue()
- Dentro do Principal class, declaramos uma referência a MyInterface . Observe que podemos declarar uma referência de uma interface, mas não podemos instanciar uma interface. Aquilo é,
// 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;
- Por fim, 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
Aqui, a variável n dentro dos parênteses é um parâmetro passado para a expressão lambda. O corpo lambda pega o parâmetro e verifica se é par ou ímpar.
Exemplo 4:usando expressão lambda com parâmetros
@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"));
}
}
Saída :
Lambda reversed = adbmaL
Interface funcional genérica
Até agora usamos a interface funcional que aceita apenas um tipo de valor. Por exemplo,
@FunctionalInterface
interface MyInterface {
String reverseString(String n);
}
A interface funcional acima aceita apenas
String
e retorna String
. No entanto, podemos tornar a interface funcional genérica, para que qualquer tipo de dado seja aceito. Se você não tiver certeza sobre genéricos, visite Java Generics. Exemplo 5:interface funcional genérica e expressões lambda
// GenericInterface.java
@FunctionalInterface
interface GenericInterface<T> {
// 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<String> 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<Integer> 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));
}
}
Saída :
Lambda reversed = adbmaL factorial of 5 = 120
No exemplo acima, criamos uma interface funcional genérica chamada GenericInterface . Ele contém um método genérico chamado
func()
. Aqui, dentro da classe Main,
GenericInterface<String> reverse
- cria uma referência para a interface. A interface agora opera emString
tipo de dados.GenericInterface<Integer> factorial
- cria uma referência para a interface. A interface, neste caso, opera noInteger
tipo de dados.
Expressão Lambda e API de fluxo
O novo pacote java.util.stream foi adicionado ao JDK8, que permite aos desenvolvedores Java realizar operações como pesquisar, filtrar, mapear, reduzir ou manipular coleções como
Lists
. Por exemplo, temos um fluxo de dados (no nosso caso um
List
de String
) onde cada string é uma combinação do nome do país e do local do país. Agora, podemos processar esse fluxo de dados e recuperar apenas os lugares do Nepal. Para isso, podemos realizar operações em massa no stream pela combinação de Stream API e expressão Lambda.
Exemplo 6:demonstração do uso de lambdas com a API Stream
import java.util.ArrayList;
import java.util.List;
public class StreamMain {
// create an object of list using ArrayList
static List<String> 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<String> 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));
}
}
Saída :
Places from Nepal: NEPAL, KATHMANDU NEPAL, POKHARA
No exemplo acima, observe a declaração,
myPlaces.stream()
.filter((p) -> p.startsWith("Nepal"))
.map((p) -> p.toUpperCase())
.sorted()
.forEach((p) -> System.out.println(p));
Aqui, estamos usando os métodos como
filter()
, map()
e forEach()
da API de fluxo. Esses métodos podem receber uma expressão lambda como entrada. 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.
Java