Lambda 表达式
你知道吗,Java 代码里 80% 的匿名内部类,其实可以只写一行。
不信?看看这个:
java
// 以前:匿名内部类
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
});
thread.start();
// 现在:Lambda 表达式
Thread thread = new Thread(() -> System.out.println("Hello"));
thread.start();从 6 行变成 1 行。这就是 Lambda 的力量。
基本语法
Lambda 的核心是一个箭头符号:->
(参数) -> { 方法体 }参数部分和以前方法声明一样,只是省略了类型(编译器会推断)。箭头后面是你要执行的代码。
几种写法
java
// 完整写法:参数类型、括号、大括号、return 全部保留
(BinaryOperator<Integer>) (int a, int b) -> {
return a + b;
};
// 省略参数类型(编译器推断)
(BinaryOperator<Integer>) (a, b) -> {
return a + b;
};
// 单行方法体:省略 return 和大括号
BinaryOperator<Integer> add = (a, b) -> a + b;
// 单参数:省略括号
Consumer<String> printer = s -> System.out.println(s);参数规则速查
| 场景 | 能省略 | 不能省略 |
|---|---|---|
| 单参数 | 括号 | — |
| 无/多参数 | — | 括号 |
| 编译器能推断类型 | 参数类型 | — |
| 需要显式声明 | — | 多参数时至少一个 |
方法引用
当你只有一个方法调用时,Lambda 可以进一步简化为方法引用。
四种类型
java
import java.util.function.*;
import java.util.*;
// 类型一:静态方法引用
// Integer.parseInt(str) → Integer::parseInt
Function<String, Integer> parser = Integer::parseInt;
parser.apply("123"); // 123
// 类型二:实例方法引用(任意对象)
// s.toUpperCase() → String::toUpperCase
Function<String, String> upper = String::toUpperCase;
upper.apply("hello"); // HELLO
// 类型三:实例方法引用(特定对象)
String str = "hello";
// str.length() → str::length
Supplier<Integer> len = str::length;
len.get(); // 5
// 类型四:构造方法引用
// new ArrayList<>() → ArrayList::new
Supplier<ArrayList<String>> factory = ArrayList::new;
ArrayList<String> list = factory.get();什么时候用方法引用
记住这个优先级:方法引用 > Lambda > 匿名内部类
java
// 同一个目标,越来越简洁
list.stream()
.filter(s -> s.isEmpty()) // Lambda
.filter(String::isEmpty) // 方法引用 ✅
.collect(Collectors.toList());
list.forEach(s -> System.out.println(s)); // Lambda
list.forEach(System.out::println); // 方法引用 ✅与匿名内部类的区别
表面相似,本质不同。
java
// 匿名内部类
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println(this.getClass()); // 指向匿名类
}
};
// Lambda
Runnable r2 = () -> {
System.out.println(this.getClass()); // this 指向外层类
};| 区别 | Lambda | 匿名内部类 |
|---|---|---|
this 指向 | 外层类 | 匿名类本身 |
| 编译产物 | invokedynamic | 生成新 .class |
| 变量作用域 | 访问 effectively final | 可修改局部变量 |
| 能定义多个方法 | ❌ | ✅ |
这里
this的差异很重要。在 Lambda 里,this不会指向 Lambda 本身,而是指向包含它的那个类——这和匿名内部类完全不同。
变量捕获
Lambda 可以"看到"外层变量,但这些变量必须是 effectively final。
java
int factor = 2; // 实际上是 final
// Lambda 读取外层变量
Function<Integer, Integer> multiply = n -> n * factor;
// multiply.apply(5) → 10
// 改一下试试?
factor = 3; // ❌ 编译错误:Lambda 引用的变量不能被修改背后的原理
java
// 编译器实际做了什么
// 1. 把外层变量"捕获"进来
// 2. 自动加上 final
// 3. 转换成匿名类能用的形式
// 你写的代码:
int x = 10;
Function<Integer, Integer> f = n -> n * x;
// 编译器内部近似生成:
int capturedX = 10; // 实际 final
new Function<Integer, Integer>() {
private final int capturedX = capturedX; // 捕获副本
@Override
public Integer apply(Integer n) {
return n * this.capturedX;
}
};实战场景
场景一:集合排序
java
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
// 按字母排序
names.sort(String::compareTo);
// 按长度排序
names.sort(Comparator.comparingInt(String::length));
// 倒序
names.sort(Comparator.reverseOrder());场景二:回调模式
java
@FunctionalInterface
interface Callback<T> {
void onComplete(T result);
}
void fetchData(Callback<String> callback) {
String data = "...";
callback.onComplete(data);
}
// Lambda 调用
fetchData(result -> System.out.println("收到: " + result));场景三:条件链
java
String input = null;
// 传统写法
String result = "";
if (input != null) {
result = input.toUpperCase();
}
// Lambda + Optional
String result2 = Optional.ofNullable(input)
.map(String::toUpperCase)
.orElse("");常见误区
误区一:过度使用
java
// ❌ 过度:3 个字的 Lambda 写成方法引用反而更难读
list.forEach(Consumer::accept); // 这是什么?
// ✅ 适度:方法引用用于简单场景
list.forEach(System.out::println);
// ✅ 适度:Lambda 用于有逻辑的场景
list.forEach(s -> {
if (s.length() > 5) {
System.out.println(s);
}
});误区二:Lambda 嵌套
java
// ❌ 嵌套太深难读
list.stream()
.filter(s -> s.stream()
.anyMatch(c -> c > 'z'))
.findAny();
// ✅ 拆成方法
list.stream()
.filter(this::hasUpperCase)
.findAny();
private boolean hasUpperCase(String s) {
return s.chars().anyMatch(c -> c > 'z');
}小结
Lambda 的学习曲线很短:
- 记住三件事:
->符号、参数推断、effectively final - 优先用方法引用,简洁且清晰
- 注意
this的指向——它指向外层类,不是 Lambda 本身 - 不要为了用 Lambda 而用它,简单的循环遍历有时更直接
