在本文中,我们将简单了解 Java 中的 Lambda 表达式。
什么是 Lambda 表达式?
Lambda 表达式是 Java 8 中引入的一个新特性,它是一种匿名函数,可以用来简化代码,使代码更加简洁和易读。但是,在讲解 Lambda 表达式之前,我们先来了解一下函数式接口(Functional Interface)。
函数式接口
简单的复习:一个接口(Interface)是一个抽象类,它定义了一组方法,但没有实现这些方法。一些类可以实现这个接口,从而实现接口中定义的方法。
如果一个 Java 接口有且仅有一个抽象方法,那么这个接口就是一个函数式接口。这唯一的一个抽象方法会定义这个接口的全部行为和功能。
例如 java.lang.Runnable
和 java.util.Comparator
都是函数式接口,因为它们都各自只有一个抽象方法(分别是 run()
和 compare()
)。
一个函数式接口的定义如下:
1 2 3 4 5 6
| import java.lang.FunctionalInterface;
@FunctionalInterface interface MyFunctionalInterface { double getValue(); }
|
在上述代码中,接口 MyFunctionalInterface
仅有一个抽象方法 double getValue()
,因此它是一个函数式接口。@FunctionalInterface
注解是可选的,不过这个注解明确的表示了这个接口是一个函数式接口,因此 Java 编译器会在编译器时检查这个接口是否符合函数式接口的定义(是否只有一个抽象方法),增加这个注解可以避免不小心添加多个抽象方法的错误,也增加了代码的可读性。
有趣的是,如果一个接口只有一个方法,那么为这个方法命名就不再非常重要了,所以 Labmda 表达式可以用来简化这个过程,省去不必要的定义。
Lambda 表达式
Lambda 表达式是一个匿名函数(anonymous function/unnamed function),其并不会直接执行,而是作为一个函数式接口中的抽象方法的实现使用。Lambda 表达式的语法如下:
1 2
| (参数列表) -> 方法体 (参数列表) -> { 方法体 }
|
运算符 ->
同时被叫做 Lambda 操作符(Lambda operator)或箭头操作符(Arrow operator),它将 Lambda 表达式的参数列表和方法体分开。
下面是一个简单的 Lambda 表达式的例子:
考虑以下代码:
1 2 3
| double getValue() { return 3.1415926; }
|
我们可以将这个传统的函数转换为 Lambda 表达式来简化这个方法的定义:
这个 Lambda 表达式的参数列表为空,方法体是 3.1415926
。
两种 Lambda 表达式的代码体
Lambda 表达式的代码体可以是一个表达式或一个代码块。
1 2 3
| () -> 3.1415926; () -> System.out.println("Hello, Lambda!");
|
上述代码中,两个 Lambda 表达式的代码体都是“表达式”,也就是说,它们都是一个单独的语句。这些语句直接写在 Lambda 表达式的右侧,不需要使用大括号 {}
。这些表达式可以是一个常量、一个变量、一个方法调用、一个对象的创建等。其返回值或值会被自动返回。
1 2 3 4 5
| () -> { double pi = 3.1415926; return pi; };
|
上述代码中,Lambda 表达式的代码体是一个代码块,代码块中包含了多个语句。在这种情况下,我们需要使用大括号 {}
来定义代码块,而且大括号结尾需要增加一个分号 ;
。如果使用代码块,我们需要使用 return
语句来返回值。
注意,我们并不需要定义返回值的类型,因为 Java 编译器可以根据上下文推断出返回值的类型,也就是说我们只需要在函数式接口中定义返回值的类型,Lambda 表达式就会自动返回这个类型的值。
由于 Lambda 表达式实际上是一个函数式接口的实现,因此我们需要提前定义这个函数式接口,然后将 Lambda 表达式作为这个接口的实现。
Lambda 表达式的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import java.lang.FunctionalInterface;
@FunctionalInterface interface MyFunctionalInterface { double getValue(); }
public class LambdaExample { public static void main(String[] args) { MyFunctionalInterface myLambda1 = () -> 3.1415926; MyFunctionalInterface myLambda2; myLambda2 = () -> 2.7182818; System.out.println("Value of Pi: " + myLambda1.getValue()); System.out.println("Value of e: " + myLambda2.getValue()); } }
|
输出结果:
1 2
| Value of Pi: 3.1415926 Value of e: 2.7182818
|
注意,上述的 MyFunctionalInterface myLambda1 = () -> 3.1415926;
并不是在创建一个新的 MyFunctionalInterface
对象(我们无法创建一个接口的对象),而是创建了一个指向 MyFunctionalInterface
的引用,这个引用指向了一个 Lambda 表达式的实现。这个引用可以调用 MyFunctionalInterface
中定义的抽象方法。
如果将上述的 Lambda 表达式改为传统的方法实现,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import java.lang.FunctionalInterface;
@FunctionalInterface interface MyFunctionalInterface { double getValue(); }
class ValueOfPi implements MyFunctionalInterface { @Override public double getValue() { return 3.1415926; } }
class ValueOfE implements MyFunctionalInterface { @Override public double getValue() { return 2.7182818; } }
public class LambdaExample { public static void main(String[] args) { ValueOfPi myLambda1 = new ValueOfPi(); MyFunctionalInterface myLambda2 = new ValueOfE(); System.out.println("Value of Pi: " + myLambda1.getValue()); System.out.println("Value of e: " + myLambda2.getValue()); } }
|
有参数的 Lambda 表达式
Lambda 表达式也可以有参数,参数列表写在 ()
中,参数之间使用逗号 ,
分隔。
1 2
| (n) -> n % 2 == 0; (a, b) -> a + b;
|
一个较为复杂的例子(返回一个反过来的字符串):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import java.lang.FunctionalInterface;
@FunctionalInterface interface ReverseString { String reverse(String str); }
public class LambdaExample { public static void main(String[] args) { ReverseString reverseString = (str) -> { String result = ""; for (int i = str.length() - 1; i >= 0; i--) { result += str.charAt(i); } return result; }; String str = "Hello, Lambda!"; System.out.println(str + " reversed is: " + reverseString.reverse(str)); } }
|
输出结果:
1
| Hello, Lambda! reversed is: !adbmaL ,olleH
|
另一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| interface MyFunctionalInterface1 { void show(); }
interface MyFunctionalInterface2 { void show(int n); }
interface MyFunctionalInterface3 { int show(int a, int b); }
interface MyFunctionalInterface4 { String show(int n); }
public class LambdaExample { public static void main(String[] args) { MyFunctionalInterface1 myLambda1 = () -> System.out.println("Hello, Lambda!"); MyFunctionalInterface2 myLambda2 = (n) -> System.out.println("Number: " + n); MyFunctionalInterface3 myLambda3 = (a, b) -> a + b; MyFunctionalInterface4 myLambda4 = (int n) -> { if (n == 1 || n == 0) { return "Not a prime number"; } for (int i = 2; i < n; i++) { if (n % i == 0) { return "Not a prime number"; } } return "Prime number"; };
myLambda1.show(); myLambda2.show(42); System.out.println("Sum of 3 and 5: " + myLambda3.show(3, 5)); System.out.println(myLambda4.show(5)); } }
|
输出结果:
1 2 3 4
| Hello, Lambda! Number: 42 Sum of 3 and 5: 8 Prime number
|
将 Lambda 表达式作为参数传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @FunctionalInterface interface MyFunctionalInterface { void calculate(int a, int b); }
public class LambdaExample { private static void operate(int a, int b, MyFunctionalInterface myFunctionalInterface) { myFunctionalInterface.calculate(a, b); }
public static void main(String[] args) { MyFunctionalInterface addition = (a, b) -> System.out.println(a + b); MyFunctionalInterface subtraction = (a, b) -> System.out.println(a - b);
operate(5, 3, addition); operate(5, 3, subtraction); } }
|
输出结果:
在上述代码中,我们定义了一个 operate
方法,这个方法接受两个整数和一个函数式接口作为参数。我们可以将 Lambda 表达式的实现作为参数传递给 operate
方法,然后在 operate
方法中调用这个函数式接口的方法。
我们其实也可以直接将 Lambda 表达式作为参数传递给方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @FunctionalInterface interface MyFunctionalInterface { void calculate(int a, int b); }
public class LambdaExample { private static void operate(int a, int b, MyFunctionalInterface myFunctionalInterface) { myFunctionalInterface.calculate(a, b); }
public static void main(String[] args) { operate(5, 3, (a, b) -> System.out.println(a + b)); operate(5, 3, (a, b) -> System.out.println(a - b)); } }
|
这两个代码片段实际上是等价的。
例题1
使用函数式接口和 Lambda 表达式,实现一个程序,确认一个数字是否存在与一个数组中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import java.util.ArrayList;
public class LambdaExample { @FunctionalInterface interface MyFunctionalInterface { boolean find(ArrayList<Integer> list, int n); }
public static void main(String[] args) { MyFunctionalInterface lambda = (list, n) -> { int left = 0, right = list.size(), mid;
while (left <= right) { mid = (left + right) / 2; if (mid >= list.size()) return false; if (list.get(mid) == n) { return true; } else if (n < list.get(mid)) { right = mid - 1; } else { left = mid + 1; } }
return false; };
ArrayList<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(2); list.add(3); list.add(3); list.add(3); list.add(3); list.add(5); System.out.println(lambda.find(list, 3) ? "Found" : "Not Found"); } }
|