Lambda 表达式,也称闭包,它允许把函数作为一个方法的参数。使用 Lambda 表达式可以使代码更加简洁。在 Java 8 以前,若我们想要把某些功能传递给某些方法,总要去写匿名类。现在用Lambda 表达式,即可以很好地解决问题。
一、lambda 表达式的语法格式及示范:
语法:
(parameters) -> expression
或
(parameters) ->{ statements; }
示范:
(int a, int b) -> { return a + b; }
() -> System.out.println("Hello World");
(String s) -> { System.out.println(s); }
() -> 42
() -> { return 3.1415 };
说明:
- Lambda 表达式可以具有零个,一个或多个参数。
- 可以显式声明参数的类型,也可以由编译器根据上下文推断。例如 (int a) 与 (a)等同。
- 参数用小括号括起来,用逗号分隔。例如 (a, b) 或 (int a, int b) 或 (String a, int b, float c)。
- 空括号用于表示一组空的参数。例如 () -> 42。
- 当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a*a。
- Lambda 表达式的正文可以包含零条,一条或多条语句。
- 如果 Lambda 表达式的正文只有一条语句,则大括号可不用写,且表达式的返回值类型要与匿名函数的返回类型相同。
- 如果 Lambda 表达式的正文有一条以上的语句,则必须包含在大括号(代码块)中,且表达式的返回值类型要与匿名函数的返回类型相同。
二、一个完整的示范
public class MyDemo {
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
public static void main(String args[]) {
MyDemo tester = new MyDemo();
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 花括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有花括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message -> System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) -> System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
}
输出如下:
%JAVA_HOME%\bin\java "MyDemo" ...
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google
三、从 Lambda 表达式到双冒号操作符
使用 Lambda 表达式,可以让代码变得非常简洁。例如,要创建一个比较器,以下语法就足够了:
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());
然后,使用类型推断,可以写成如下形式:
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());
更进一步,可以用如下写法:
Comparator c = Comparator.comparing(Person::getAge);
双冒号(::)操作符是 Java 中的方法引用。 当们使用一个方法的引用时,目标引用放在 :: 之前,目标引用提供的方法名称放在 :: 之后。
四、更多Lambda 表达式的例子
1. 线程初始化
// Old way
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world");
}
}).start();
// New way
new Thread(
() -> System.out.println("Hello world")
).start();
2. 事件处理
// Old way
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello world");
}
});
// New way
button.addActionListener( (e) -> {
System.out.println("Hello world");
});
3. 遍历输出(方法引用)
import java.util.*;
public class MyDemo {
public static void main(String args[]) {
// Old way
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for (Integer n : list) {
System.out.print(n + " ");
}
System.out.println();
// 使用 -> 的 Lambda 表达式
list.forEach(n -> System.out.print(n + " "));
System.out.println();
// 使用 :: 的 Lambda 表达式
list.forEach(System.out::print);
}
}
4. 逻辑操作
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class MyDemo {
public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
for (Integer n : list) {
if (predicate.test(n)) {
System.out.print(n + " ");
}
}
System.out.println();
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
System.out.print("输出所有数字:");
evaluate(list, (n) -> true);
System.out.print("不输出:");
evaluate(list, (n) -> false);
System.out.print("输出偶数:");
evaluate(list, (n) -> n % 2 == 0);
System.out.print("输出奇数:");
evaluate(list, (n) -> n % 2 == 1);
System.out.print("输出大于 5 的数字:");
evaluate(list, (n) -> n > 5);
}
}
4. Stream API 示例
// Old way
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
int x = n * n;
System.out.println(x);
}
// New way
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);
五、Lambda 表达式和匿名类之间的区别
- this 关键字。对于匿名类 this 关键字解析为匿名类,而对于 Lambda 表达式,this 关键字解析为包含写入 Lambda 的类。
- 编译方式。Java 编译器编译 Lambda 表达式时,会将其转换为类的私有方法,再进行动态绑定。