English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Neste artigo, vamos entender a expressão lambda Java por meio de exemplos, bem como o uso de interfaces funcionais, interfaces genéricas de funções e API de fluxo.
A expressão lambda é no Java 8Pela primeira vez introduzido. Seu principal objetivo é melhorar a capacidade de expressão do idioma.
Mas, antes de aprender lambda, precisamos primeiro entender as interfaces funcionais.
Se o interface Java contiver apenas um método abstrato, ele é chamado de interface funcional. Apenas esse método especifica o uso esperado da interface.
例如,包java.lang中的Runnable接口;是一个功能接口,因为它只包含一个方法,即run()。
import java.lang.FunctionalInterface; @FunctionalInterface public interface MyInterface{ //单一抽象方法 double getValue(); }
在上面的示例中,接口MyInterface只有一个抽象方法getValue()。因此,它是一个功能接口。
在这里,我们使用了注解@FunctionalInterface。该注解会强制Java编译器指示该接口是功能接口。因此,不允许有多个抽象方法。但是,它不是强制性的。
在Java 7中,功能接口被视为单一抽象方法(SAM)类型。在Java 7中,SAM类型通常是通过匿名类实现的。
public class FunctionInterfaceTest { public static void main(String[] args) { //匿名类 new Thread(new Runnable() { @Override public void run() { System.out.println("我刚刚实现了Runnable功能接口。"); } }).start(); } }
Saída:
我刚刚实现了Runnable功能接口。
在这里,我们可以将匿名类传递给方法。这有助于用Java 7编写更少的程序。但是,语法仍然很困难,需要大量的额外代码行。
Java 8进一步扩展了SAM的功能。由于我们知道功能接口只有一个方法,因此在将其作为参数传递时,无需定义该方法的名称。Lambda表达式使我们能够做到这一点。
Lambda表达式本质上是一个匿名或未命名的函数。Lambda表达式不能单独执行。相反,它用于实现功能接口定义的方法。
这是我们如何在Java中定义lambda表达式。
(lista de parâmetros) -> corpo do lambda
使用的新运算符(->) chamado operador de seta ou operador lambda. Vamos explorar alguns exemplos,
假设我们有一个这样的方法:
double getPiValue() { return 3.1415; }
Podemos escrever este método usando a expressão lambda, conforme mostrado:
(); -> 3.1415
Aqui, o método não tem nenhum parâmetro. Portanto, o lado esquerdo do operador inclui um parâmetro vazio. O lado direito é o corpo de lambda, que especifica a operação da expressão lambda. Neste caso, ele retornará o valor3.1415。
No Java, os corpos de lambda têm dois tipos.
1. Corpo de expressão único
(); -> System.out.println("Lambdas são ótimas");
Este tipo de corpo de lambda é chamado de corpo de expressão.
2. Corpo principal composto por código.
(); -> { double pi = 3.1415; return pi; };
Este tipo de corpo de lambda é chamado de corpo de bloco. O corpo de bloco permite que o corpo de lambda contenha várias instruções. Essas instruções estão entre parênteses e você deve adicionar um ponto e vírgula após os parênteses.
Atenção: Para o corpo do bloco, você sempre deve ter uma instrução return. No entanto, um corpo de expressão única não precisa de instrução return.
Vamos escrever um programa Java que usa a expressão lambda para retornar o valor de Pi.
Como mencionado anteriormente, a expressão lambda não é executada isoladamente. Em vez disso, ela forma a implementação do método abstrato definido pela interface funcional.
Portanto, precisamos definir primeiro uma interface funcional.
import java.lang.FunctionalInterface; //Isso é uma interface funcional @FunctionalInterface interface MyInterface{ // 抽象方法 double getPiValue(); } public class Main { public static void main(String[] args) { //声明对MyInterface的引用 MyInterface ref; // expressão lambda ref = () -> 3.1415; System.out.println("Pi = "); + ref.getPiValue()); } }
Saída:
Pi = 3.1415
No exemplo acima,
Criamos uma interface funcional chamada MyInterface. Ela contém um método abstrato chamado getPiValue().
Dentro da classe Main, declaramos uma referência para MyInterface. Observe que podemos declarar uma referência para a interface, mas não instanciar a interface. Isso é porque,
//Isso lançará um erro MyInterface ref = new myInterface(); // Isso é válido MyInterface ref;
Em seguida, atribuímos uma expressão lambda à referência.
ref = () -> 3.1415;
Por fim, usamos o método getPiValue() da interface reference.
System.out.println("Pi = "); + ref.getPiValue());
Até agora, já criamos expressões lambda sem qualquer parâmetro. Mas, como métodos, as expressões lambda também podem ter parâmetros. Por exemplo,
(n) -> (n%2)=0
在此,括号内的变量n是传递给lambda表达式的参数。Lambda主体接受参数并检查其是偶数还是奇数。
@FunctionalInterface interface MyInterface { //抽象方法 String reverse(String n); } public class Main { public static void main(String[] args) { //声明对MyInterface的引用 //将lambda表达式分配给引用 MyInterface ref = (str) -> { String result = ""; for (int i = str.length();-1; i >= 0; i--){ result += str.charAt(i); } return result; }; //调用接口的方法 System.out.println("Lambda reversed = " + ref.reverse("Lambda")); } }
Saída:
Lambda reversed = adbmaL
到目前为止,我们已经使用了仅接受一种类型的值的功能接口。例如,
@FunctionalInterface interface MyInterface { String reverseString(String n); }
上面的功能接口仅接受String并返回String。但是,我们可以使功能接口通用,以便接受任何数据类型。如果不熟悉泛型,请访问Java泛型。
// GenericInterface.java @FunctionalInterface interface GenericInterface<T> { // 泛型方法 T func(T t); } // GenericLambda.java public class Main { public static void main(String[] args) { //声明对GenericInterface的引用 // GenericInterface对String数据进行操作 //为其分配一个lambda表达式 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")); //声明对GenericInterface的另一个引用 // GenericInterface对整数数据进行操作 //为其分配一个lambda表达式 GenericInterface<Integer> factorial = (n) -> { int result = 1; for (int i = 1; i <= n; i++) result = i * result; return result; }; System.out.println("5fatorial = " + factorial.func(5)); } }
Saída:
Lambda reversed = adbmaL 5fatorial = 120
No exemplo acima, criamos uma interface funcional genérica chamada GenericInterface. Ela contém um método genérico chamado func().
Dentro da classe:
GenericInterface<String> reverse - Criar uma referência para essa interface. Agora, a interface pode manipular dados do tipo String.
GenericInterface<Integer> factorial -Criar uma referência para essa interface. Neste caso, a interface opera com dados do tipo Integer.
novosjava.util.streampacote foi adicionado ao JDK8que permite que desenvolvedores Java executem buscas, filtros, mapeamentos, reduções e operações em listas e outros conjuntos.
Por exemplo, temos um fluxo de dados (em nosso exemplo, uma lista de strings), onde cada string é o nome do país e do/Combinação de regiões. Agora, podemos processar esse fluxo de dados e recuperar apenas localizações do Nepal.
Para isso, podemos combinar o API Stream e a expressão lambda para executar operações em lote no fluxo.
import java.util.ArrayList; import java.util.List; public class StreamMain { //Usar ArrayList para criar um objeto de lista static List<String> places = new ArrayList<>(); //Preparar nossos dados public static List getPlaces(){ //Adicionar localidades e países à lista 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("Lugares de Nepal:"); myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p)); } }
Saída:
Lugares de Nepal: NEPAL, KATHMANDU NEPAL, POKHARA
Neste exemplo, note as seguintes declarações:
myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p));
Aqui, usamos métodos como filter(), map() e forEach() da API Stream. Esses métodos podem usar expressões lambda como entrada.
Podemos definir nossas próprias expressões com base na sintaxe estudada acima. Como mostrado no exemplo, isso nos permite reduzir significativamente o número de linhas de código.