lambda 表达式是 Java 8 引入的重要特性,在 Java 8 及后续版本中,lambda 表达式是一项核心特性,它通过简洁的语法大幅简化了函数式接口的实现。作为匿名函数的轻量级替代方案,lambda 表达式无需显式定义类即可直接表示行为参数化,特别适用于单抽象方法(SAM)接口的场景。
基本语法格式
lambda 表达式的基本语法格式如下:
(parameters) -> expression
或
(parameters) -> { statements; }
下面是对语法格式中各部分的详细说明:
- 参数列表:可以是零个、一个或者多个参数。当参数只有一个时,括号
()
可以省略;没有参数时,必须使用空括号()
。 - 箭头符号:由
-
和>
组合而成,是 lambda 表达式的操作符,它将参数列表与 lambda 体分隔开来。 - lambda 体:可以是一个表达式或者一个语句块。若为表达式,表达式的值会作为返回值;若为语句块,则需要使用花括号
{}
将语句括起来,若有返回值,还需要使用return
语句。
语法实现的详细分类
1. 无参数,返回值为表达式
// 无参数,返回一个字符串
() -> "Hello Lambda!";
这是一个没有参数的 lambda 表达式,它返回一个字符串常量"Hello Lambda!"
。
2. 无参数,lambda 体为语句块
// 无参数,lambda体为一个语句块
() -> {
System.out.println("Starting...");
System.out.println("Processing...");
return "Completed";
};
此 lambda 表达式同样没有参数,但其 lambda 体是一个包含多个语句的语句块,最后返回"Completed"
。
3. 单个参数,参数类型可省略
// 单个参数,参数类型可省略
x -> x * x;
// 等同于
(int x) -> x * x;
当 lambda 表达式只有一个参数时,参数的类型可以省略。这里的 lambda 表达式接收一个参数x
,并返回x
的平方。
4. 多个参数,需用括号括起来
// 多个参数,需用括号括起来
(x, y) -> x + y;
// 等同于
(int x, int y) -> x + y;
对于有多个参数的 lambda 表达式,参数列表必须用括号括起来,参数类型可以指定,也可以省略。该 lambda 表达式接收两个参数x
和y
,并返回它们的和。
5. 多个参数,有返回值和语句块
// 多个参数,有返回值和语句块
(int a, int b) -> {
int result = a + b;
return result;
};
这个 lambda 表达式接收两个整型参数a
和b
,在语句块中计算它们的和并存储在result
变量中,最后返回该结果。
6. 参数类型声明
// 显式声明参数类型
(String s) -> s.length();
在 lambda 表达式中,可以显式地声明参数的类型,这里声明了参数s
的类型为String
,并返回其长度。
7. 无参数,无返回值
// 无参数,无返回值
() -> System.out.println("Hello");
此 lambda 表达式没有参数,也没有返回值,它只是简单地打印"Hello"
。
8. 单个参数,无返回值
// 单个参数,无返回值
s -> System.out.println(s);
该 lambda 表达式接收一个参数s
,并将其打印输出,没有返回值。
函数式接口与 lambda 表达式
函数式接口是指仅包含一个抽象方法的接口,它是 lambda 表达式的使用前提。lambda 表达式实际上是函数式接口的一个实例。
Java 提供了一些内置的函数式接口,例如:
- Predicate<T>:接收一个参数,返回一个布尔值。
Predicate<Integer> isEven = num -> num % 2 == 0;
这里的 lambda 表达式实现了Predicate
接口的test
方法,用于判断一个整数是否为偶数。
- Consumer<T>:接收一个参数,不返回值。
Consumer<String> printer = s -> System.out.println(s);
此 lambda 表达式实现了Consumer
接口的accept
方法,用于消费一个字符串并打印它。
- Function<T, R>:接收一个参数,返回一个结果。
Function<String, Integer> lengthFunction = s -> s.length();
该 lambda 表达式实现了Function
接口的apply
方法,用于计算字符串的长度。
- Supplier<T>:不接收参数,返回一个值。
Supplier<Double> randomSupplier = () -> Math.random();
这里的 lambda 表达式实现了Supplier
接口的get
方法,用于生成一个随机数。
变量捕获
lambda 表达式可以捕获外部的局部变量、实例变量和静态变量,但对局部变量有特殊要求,即局部变量必须是final
的或者实际上是final
的(即一旦赋值后就不会再被修改)。
捕获局部变量
int factor = 10;
Function<Integer, Integer> multiplier = num -> num * factor;
在这个例子中,lambda 表达式捕获了外部的局部变量factor
,并且factor
在赋值后不能再被修改,否则会导致编译错误。
捕获实例变量和静态变量
public class LambdaExample {
private int instanceVar = 10;
private static int staticVar = 20;
public void testLambda() {
// 捕获实例变量
Consumer<Integer> instanceConsumer = num -> System.out.println(num * instanceVar);
// 捕获静态变量
Consumer<Integer> staticConsumer = num -> System.out.println(num * staticVar);
}
}
lambda 表达式捕获实例变量和静态变量时,没有局部变量那样的限制,因为实例变量和静态变量的生命周期与 lambda 表达式的生命周期可能不同。
方法引用与构造函数引用
lambda 表达式可以进一步简化为方法引用或构造函数引用。
方法引用
// 使用lambda表达式
Consumer<String> printer1 = s -> System.out.println(s);
// 使用方法引用
Consumer<String> printer2 = System.out::println;
方法引用是 lambda 表达式的一种更简洁的形式,当 lambda 表达式只是调用一个已存在的方法时,可以使用方法引用。
构造函数引用
// 使用lambda表达式
Supplier<List<String>> listSupplier1 = () -> new ArrayList<>();
// 使用构造函数引用
Supplier<List<String>> listSupplier2 = ArrayList::new;
构造函数引用用于创建对象实例,它是 lambda 表达式创建对象的一种简化形式。
注意事项
- 类型推断:Java 编译器会根据上下文推断 lambda 表达式的参数类型,因此通常不需要显式声明参数类型。
- this 关键字:lambda 表达式中的
this
关键字引用的是创建该 lambda 表达式的对象,而不是 lambda 表达式本身。 - 异常处理:如果函数式接口的抽象方法声明了异常,lambda 表达式中也需要处理相应的异常。
掌握 lambda 表达式的语法和使用场景,可以使 Java 代码更加简洁、灵活,提高开发效率。