Lambda 表达式是 Java 8 引入的一项重要特性,它提供了一种简洁的方式 来表示匿名函数(即没有名字的函数) ,常用于实现函数式接口(只有一个抽象方法的接口)。在 Java 中,Lambda 表达式主要用于简化回调、事件处理、集合操作等场景。
🧩 一、基本语法
几种常见写法:
| 场景 | Lambda 写法 | 说明 |
|---|---|---|
| 无参无返回 | () -> System.out.println("Hello") |
最简形式 |
| 一个参数 | (x) -> x * 2 或 x -> x * 2 |
单个参数可省略括号 |
| 多个参数 | (x, y) -> x + y |
必须加括号 |
| 多行语句 | (x) -> { int r = x * 2; return r; } |
需要大括号和 return |
| 有返回值(单表达式) | x -> x * 2 |
自动返回表达式结果 |
📌 二、使用前提:函数式接口
Lambda 只能用于函数式接口(Functional Interface),即:
- 接口中有且仅有一个抽象方法
- 可用
@FunctionalInterface注解(非必须,但推荐)
常见内置函数式接口(都在 java.util.function 包中):
| 接口 | 方法 | 用途 |
|---|---|---|
Runnable |
void run() |
无参无返回(如线程任务) |
Consumer<T> |
void accept(T t) |
消费一个参数,无返回 |
Supplier<T> |
T get() |
无参,有返回 |
Function<T, R> |
R apply(T t) |
有参有返回 |
Predicate<T> |
boolean test(T t) |
判断条件(返回 boolean) |
✅ 这些接口你不需要自己写,直接用!
🧪 三、实际例子
1. 线程启动(使用 Runnable)
// 传统方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("老写法");
}
}).start();
// Lambda 方式
new Thread(() -> System.out.println("Lambda 启动线程!")).start();
2. 集合遍历(使用 Consumer)
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 传统 for-each
for (String name : names) {
System.out.println(name);
}
// 使用 Lambda + forEach
names.forEach(name -> System.out.println(name));
// 更简:方法引用
names.forEach(System.out::println);
3. 过滤数据(使用 Predicate)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 找出偶数
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0) // Predicate<Integer>
.collect(Collectors.toList());
System.out.println(evens); // [2, 4, 6]
4. 自定义回调
@FunctionalInterface
interface Callback {
void call(String message);
}
public static void doTask(Callback cb) {
System.out.println("执行任务中...");
cb.call("任务完成!");
}
// 调用
doTask(msg -> System.out.println("收到: " + msg));
5. 返回值(使用 Function)
Function<String, Integer> strToInt = s -> Integer.parseInt(s);
int num = strToInt.apply("123");
System.out.println(num + 1); // 124
⚠️ 四、注意事项
-
Lambda 不能单独存在,必须赋值给一个函数式接口类型。
// ❌ 错误:不能这样写 x -> x + 1; // ✅ 正确 Function<Integer, Integer> f = x -> x + 1; -
变量捕获(闭包):
-
Lambda 可以访问外部局部变量,但该变量必须是 effectively final(实际上不可变)
int factor = 10; // 实际上 final(没被修改)
Function<Integer, Integer> multiply = x -> x * factor;
-
-
不能修改外部局部变量:
int count = 0; Runnable r = () -> count++; // ❌ 编译错误!
🔁 五、Lambda vs 匿名内部类
| 特性 | 匿名内部类 | Lambda |
|---|---|---|
| 语法 | 冗长 | 简洁 |
| 性能 | 每次创建新类 | JVM 优化(可能复用) |
| 适用范围 | 任意接口/抽象类 | 仅函数式接口 |
this 指向 |
匿名类实例 | 外部类实例 |
✅ 总结
Lambda 表达式让你用更少的代码做更多的事,尤其适合:
- 回调函数
- 集合流式操作(
stream().map().filter().forEach()) - 事件监听
- 并发任务(如
ExecutorService.submit(() -> {...}))
💡 记住口诀:"左边参数,右边逻辑,中间箭头" →
(params) -> { body }
(参数列表) -> { 函数体 }