🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# Java Lambda 表达式 > 原文: [https://www.programiz.com/java-programming/lambda-expression](https://www.programiz.com/java-programming/lambda-expression) #### 在本文中,我们将借助示例学习 Java lambda 表达式以及 lambda 表达式与函数式接口,通用函数式接口和流 API 的结合使用。 lambda 表达式是在 Java 8 中首次引入的。其主要目的是提高语言的表达能力。 但是,在进入 lambda 之前,我们首先需要了解函数式接口。 * * * ## 什么是函数式接口? 如果 Java 接口仅包含一个抽象方法,则将其称为函数式接口。 仅这一种方法指定了接口的预期用途。 例如,包`java.lang`中的`Runnable`接口; 由于它仅构成一种方法,即`run()`,因此它是一个函数式接口。 ### 示例 1:在 java 中定义函数式接口 ```java import java.lang.FunctionalInterface; @FunctionalInterface public interface MyInterface{ // the single abstract method double getValue(); } ``` 在上面的示例中,接口`MyInterface`只有一个抽象方法`getValue()`。 因此,它是一个函数式接口。 在这里,我们使用了注解`@FunctionalInterface`。 该注解会强制 Java 编译器指示该接口是函数式接口。 因此,不允许有多个抽象方法。 但是,它不是强制性的。 在 Java 7 中,函数式接口被视为[单一抽象方法](https://stackoverflow.com/a/23342625/4494547)或 **SAM** 类型。 SAM 通常用 Java 7 中的匿名类实现。 ### 示例 2:使用 Java 中的匿名类实现 SAM ```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(); } } ``` **输出**: ```java I just implemented the Runnable Functional Interface. ``` 在这里,我们可以将匿名类传递给方法。 这有助于用 Java 7 用更少的代码编写程序。但是,语法仍然很困难,并且需要很多额外的代码行。 Java 8 进一步扩展了 SAM 的功能。 由于我们知道函数式接口只有一种方法,因此在将其作为参数传递时,无需定义该方法的名称。 Lambda 表达式使我们能够做到这一点。 * * * ## Lambda 表达式简介 Lambda 表达式本质上是一个匿名或未命名的方法。 lambda 表达式不能单独执行。 相反,它用于实现函数式接口定义的方法。 ### 如何在 Java 中定义 Lambda 表达式? 这是我们如何在 Java 中定义 lambda 表达式。 ```java (parameter list) -> lambda body ``` 使用的新运算符(`->`)被称为箭头运算符或 lambda 运算符。 目前语法尚不清楚。 让我们探索一些例子, 假设我们有一个这样的方法: ```java double getPiValue() { return 3.1415; } ``` 我们可以使用 lambda 表达式编写此方法,如下所示: ```java () -> 3.1415 ``` 在此,该方法没有任何参数。 因此,运算符的左侧包括一个空参数。 右侧是 lambda 主体,用于指定 lambda 表达式的操作。 在这种情况下,它将返回值 3.1415。 * * * ### Lambda 主体的类型 在 Java 中,lambda 主体有两种类型。 **1.具有单个表达式的正文** ```java () -> System.out.println("Lambdas are great"); ``` 这种类型的 lambda 主体称为表达式主体。 **2.由代码块组成的正文** ```java () -> { double pi = 3.1415; return pi; }; ``` 这种类型的 lambda 体称为块体。 块主体允许 lambda 主体包含多个语句。 这些语句包含在括号内,您必须在括号后添加分号。 **注意**:对于块体,您应该始终有一个`return`语句。 但是,表达式主体不需要`return`语句。 * * * ## 示例 3:Lambda 表达式 让我们编写一个 Java 程序,该程序使用 lambda 表达式返回`Pi`的值。 如前所述,lambda 表达式不是单独执行的。 相反,它形成了由函数式接口定义的抽象方法的实现。 因此,我们需要首先定义一个函数式接口。 ```java 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()); } } ``` **输出**: ```java Value of Pi = 3.1415 ``` 在上面的示例中, * 我们创建了一个名为`MyInterface`的函数式接口。 它包含一个名为`getPiValue()`的抽象方法 * 在`Main`类中,我们声明了对`MyInterface`的引用。 请注意,我们可以声明接口的引用,但不能实例化接口。 即 , ```java // it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref; ``` * 然后,我们为引用分配了一个 lambda 表达式。 ```java ref = () -> 3.1415; ``` * 最后,我们使用引用接口调用方法`getPiValue()`。 当 时 ```java System.out.println("Value of Pi = " + ref.getPiValue()); ``` * * * ## 带参数的 Lambda 表达式 到目前为止,我们已经创建了不带任何参数的 lambda 表达式。 但是,类似于方法,lambda 表达式也可以具有参数。 例如, ```java (n) -> (n%2)==0 ``` 在此,括号内的变量`n`是传递给 lambda 表达式的参数。 Lambda 主体接受参数并检查其是偶数还是奇数。 ### 示例 4:将 lambda 表达式与参数一起使用 ```java @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")); } } ``` **输出**: ```java Lambda reversed = adbmaL ``` * * * ## 泛型函数式接口 到目前为止,我们已经使用了仅接受一种类型的值的函数式接口。 例如, ```java @FunctionalInterface interface MyInterface { String reverseString(String n); } ``` 上面的函数式接口仅接受`String`并返回`String`。 但是,我们可以使函数式接口通用,以便接受任何数据类型。 如果您不确定泛型,请访问 [Java 泛型](/java-programming/generics)。 ### 示例 5:泛型函数式接口和 Lambda 表达式 ```java // 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)); } } ``` **输出**: ```java Lambda reversed = adbmaL factorial of 5 = 120 ``` 在上面的示例中,我们创建了一个名为`GenericInterface`的通用函数式接口。 它包含一个名为`func()`的通用方法。 在`Main`类里面 * `GenericInterface<String> reverse` - 创建对接口的引用。 现在,该接口可处理`String`类型的数据。 * `GenericInterface<Integer> factorial` - 创建对接口的引用。 在这种情况下,该接口对`Integer`类型的数据进行操作。 * * * ## Lambda 表达式和流 API 新的[`java.util.stream`](http://javadocs.techempower.com/jdk18/api/java/util/stream/package-summary.html)包已添加到 JDK8,它允许 Java 开发人员执行诸如`Lists`之类的搜索,过滤,映射,归约或操作集合之类的操作。 例如,我们有一个数据流(在我们的示例中为`String`的`List`),其中每个字符串都是国家名称和国家/地区的组合。 现在,我们可以处理此数据流,并且仅从尼泊尔检索位置。 为此,我们可以结合使用流 API 和 Lambda 表达式在流中执行批量操作。 ### 示例 6:将 lambda 与流 API 一起使用的示例 ```java 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)); } } ``` **输出**: ```java Places from Nepal: NEPAL, KATHMANDU NEPAL, POKHARA ``` 在上面的示例中,请注意以下语句: ```java myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p)); ``` 在这里,我们使用的是流 API 的`filter()`,`map()`和`forEach()`之类的方法。 这些方法可以将 lambda 表达式作为输入。 我们还可以根据上面学习的语法定义自己的表达式。 如上例所示,这使我们可以大大减少代码行。