1. Lambda 表达式是什么
Lambda 表达式是 Java 中一种简化写法,用来快速创建某个接口的实现对象。
它主要用于替代匿名内部类。
例如以前创建 Runnable:
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("执行任务");
}
};
使用 Lambda 后:
Runnable task = () -> {
System.out.println("执行任务");
};
两者本质相同,都是在实现 Runnable 接口中的 run() 方法。
2. Lambda 的基本格式
(参数列表) -> { 方法体 }
可以理解为:
参数 -> 执行逻辑
例如:
(a, b) -> {
return a + b;
}
表示接收 a、b 两个参数,返回它们相加的结果。
3. Lambda 的常见写法
3.1 无参数、无返回值
() -> {
System.out.println("执行任务");
}
常用于 Runnable。
对应接口方法:
void run();
3.2 一个参数、无返回值
blog -> {
System.out.println(blog.getTitle());
}
常用于 forEach()。
例如:
records.forEach(blog -> {
System.out.println(blog.getTitle());
});
这里的 blog 是集合中每次遍历到的一个元素。
等价于普通循环:
for (Blog blog : records) {
System.out.println(blog.getTitle());
}
3.3 多个参数、有返回值
(a, b) -> a + b
例如排序:
list.sort((a, b) -> Integer.compare(a, b));
这里 Lambda 接收两个元素 a、b,并返回比较结果。
3.4 一个参数、有返回值
value -> Long.valueOf(value)
表示:
接收一个
value,把它转换为Long后返回。
完整写法:
value -> {
return Long.valueOf(value);
}
4. 单表达式为什么可以省略 return
下面这种写法:
value -> Long.valueOf(value)
右侧只有一条表达式:
Long.valueOf(value)
Java 会自动把这条表达式的结果作为返回值。
它等价于:
value -> {
return Long.valueOf(value);
}
规则:
x -> x * 2
等价于:
x -> {
return x * 2;
}
但是,只要写了 {},就必须手动写 return:
x -> {
return x * 2;
}
下面是错误的:
x -> {
x * 2;
}
因为加上 {} 后,Java 不会自动返回最后一行结果。
5. Lambda 只能用于函数式接口
Lambda 只能用于函数式接口。
函数式接口指:
接口中只有一个抽象方法。
例如:
public interface Runnable {
void run();
}
只有一个抽象方法,因此可以使用 Lambda:
Runnable task = () -> System.out.println("执行任务");
再例如:
public interface Callable<V> {
V call() throws Exception;
}
可以写:
Callable<Integer> task = () -> 10 + 20;
因为 Callable<Integer> 的核心方法是:
Integer call();
6. 常见函数式接口
| 接口 | 核心方法 | 参数 | 返回值 | 常见用途 |
|---|---|---|---|---|
Runnable |
run() |
无 | 无 | 线程任务 |
Callable<V> |
call() |
无 | 有 | 有返回值的线程任务 |
Consumer<T> |
accept(T t) |
1个 | 无 | forEach() |
Function<T, R> |
apply(T t) |
1个 | 有 | map() |
Predicate<T> |
test(T t) |
1个 | boolean |
filter() |
Comparator<T> |
compare(T a, T b) |
2个 | int |
排序 |
7. Lambda 的具体含义由外部接口决定
Lambda 本身不能脱离上下文单独存在。
例如:
value -> Long.valueOf(value)
Java 要先看它被赋值给谁,才能确定:
-
value是什么类型 -
返回值是什么类型
-
它是在实现哪个方法
例如:
Function<String, Long> function = value -> Long.valueOf(value);
Java 会推断为:
Long apply(String value)
等价于:
Function<String, Long> function = new Function<String, Long>() {
@Override
public Long apply(String value) {
return Long.valueOf(value);
}
};
所以 Lambda 的参数类型、返回类型、对应方法,都是由目标函数式接口决定的。
8. forEach 中的 Lambda
例如:
records.forEach(blog -> {
Long userId = blog.getUserId();
User user = userService.getById(userId);
blog.setName(user.getNickName());
blog.setIcon(user.getIcon());
isBlogLiked(blog);
});
forEach() 需要的是:
Consumer<T>
其核心方法是:
void accept(T t);
因此:
blog -> {
...
}
本质上是在实现:
void accept(Blog blog)
因为 Consumer 返回值是 void,所以不需要 return。
9. map 中的 Lambda
例如:
List<Long> ids = top5.stream()
.map(value -> Long.valueOf(value))
.collect(Collectors.toList());
执行过程:
Set<String>
↓ stream()
Stream<String>
↓ map(value -> Long.valueOf(value))
Stream<Long>
↓ collect(Collectors.toList())
List<Long>
map() 的作用是:
将流中的每个元素转换成新的元素。
其中:
value -> Long.valueOf(value)
表示:
"101" -> 101L
"205" -> 205L
"309" -> 309L
map() 需要的是:
Function<T, R>
核心方法:
R apply(T t);
因此这里相当于:
Function<String, Long>
即:
Long apply(String value)
10. map 与 forEach 的区别
forEach:处理元素,不产生新结果
list.forEach(value -> {
System.out.println(value);
});
适合做:
-
打印
-
修改对象属性
-
调用某个业务方法
-
写日志
它不负责返回新集合。
map:转换元素,产生新结果
list.stream()
.map(value -> Long.valueOf(value))
.collect(Collectors.toList());
适合做:
-
String转Long -
User转UserDTO -
Blog转标题字符串 -
数字转平方值
例如:
user -> BeanUtil.copyProperties(user, UserDTO.class)
表示将每一个 User 转换为 UserDTO。
11. Lambda 与普通 for 循环的对应关系
Stream 写法:
List<Long> ids = top5.stream()
.map(value -> Long.valueOf(value))
.collect(Collectors.toList());
普通循环写法:
List<Long> ids = new ArrayList<>();
for (String value : top5) {
Long id = Long.valueOf(value);
ids.add(id);
}
对应关系:
| Stream 写法 | 普通循环含义 |
|---|---|
stream() |
开始遍历集合 |
map() |
对每个元素做转换 |
value -> Long.valueOf(value) |
当前元素转换为 Long |
collect(toList()) |
收集成 List |
12. 方法引用
下面两种写法等价:
.map(value -> Long.valueOf(value))
.map(Long::valueOf)
因为 Lambda 的逻辑只是:
接收 value
调用 Long.valueOf(value)
返回结果
因此可以写成方法引用:
类名::方法名
例如:
.map(Long::valueOf)
初学阶段可以优先理解 Lambda 写法,熟悉后再使用方法引用。
13. 记忆总结
Lambda 的核心格式:
(参数) -> { 逻辑 }
它本质上是:
对函数式接口中唯一抽象方法的简写实现。
常见形式:
// 无参数、无返回值
() -> System.out.println("执行任务");
// 一个参数、无返回值
user -> System.out.println(user.getName());
// 一个参数、有返回值
value -> Long.valueOf(value);
// 两个参数、有返回值
(a, b) -> Integer.compare(a, b);
一句话总结:
Lambda 表达式不是独立的方法,而是根据外部需要的函数式接口,自动推断参数、返回值和对应方法的一种简写写法。