元数据卡
- 前置知识:第8+章全章(Lambda、函数式接口、Stream API)
- 预计时间:20 分钟
- 阅读模式:动手实操
- 完成标志:通过全部热身题,至少完成一个挑战题
你的进度
"光说不练,等于没学。"老陈师傅把一块刻着 Lambda 的羽毛拍在桌上。
你已经学完了函数式接口、Lambda 表达式、方法引用、Stream API。工具箱里有了这些新工具,该把它们用起来了。
热身(10 分钟,必做)
1. Stream 字符串处理
给你一个字符串列表,用 Stream 完成:
- 过滤出长度大于 3 的
- 全部转大写
- 排序
- 打印每个元素
List<String> words = List.of("猫", "老虎", "大象", "长颈鹿", "狗");
words.stream()
.filter(w -> w.length() > 3) // 过滤
.map(String::toUpperCase) // 转大写(方法引用!)
.sorted() // 排序
.forEach(System.out::println); // 打印语言:Java 8+ 预期输出:大象 → 长颈鹿
2. 匿名内部类 → Lambda
把下面这个匿名内部类改写成 Lambda:
// 改写前
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("任务执行中");
}
};
// 改写后
Runnable task = () -> System.out.println("任务执行中");你试试:改成 Lambda 后,还能改成方法引用吗?
3. Lambda → 方法引用
把下面这个 Lambda 改写成方法引用:
Function<String, Integer> f = s -> Integer.parseInt(s);
// 改写后
Function<String, Integer> f = Integer::parseInt;挑战(15 分钟,选做)
1. Stream 数据报告
给定一个 Villager 列表(字段:name, age, village),用 Stream API 完成:
record Villager(String name, int age, String village) {}
List<Villager> villagers = List.of(
new Villager("张三", 25, "变量村"),
new Villager("李四", 17, "变量村"),
new Villager("王五", 30, "算法森林"),
new Villager("赵六", 22, "变量村")
);
// 1. 找出所有来自"变量村"的村民
// 2. 按年龄排序
// 3. 只保留名字
// 4. 收集成 List<String>
// 5. 计算所有人的平均年龄
List<String> names = villagers.stream()
.filter(v -> v.village().equals("变量村"))
.sorted(Comparator.comparingInt(Villager::age))
.map(Villager::name)
.collect(Collectors.toList());
double avgAge = villagers.stream()
.mapToInt(Villager::age)
.average()
.orElse(0.0);你试试:想想如果只用 stream() 一次完成(不重复遍历),应该怎么做?提示:用 collect(Collectors.teeing(...)) 或用两次遍历。
2. 自定义函数式接口
写一个 Transformer<T, R> 接口,用 @FunctionalInterface 注解,然后创建一个数组,每个元素是一个不同的转换 Lambda,循环调用所有转换:
@FunctionalInterface
interface Transformer<T, R> {
R transform(T input);
}
// 使用
Transformer<String, Integer> toLength = s -> s.length();
Transformer<String, String> toUpper = String::toUpperCase;
Transformer<String, String> withPrefix = s -> "[消息] " + s;
List.of(toLength, toUpper, withPrefix).forEach(t -> {
System.out.println(t.transform("hello"));
});验收标准
- 能写出 Lambda 表达式(无参、单参、多参)
- 能解释"函数式接口"和 Lambda 的关系——插座与插头
- 能用方法引用替换简单的 Lambda:
System.out::println、String::length、Integer::parseInt - 能用 Stream API 完成"过滤-转换-排序-收集"四步流水线
- 能区分中间操作和终端操作
常见卡点
"Lambda 和匿名内部类一样吗?" 不一样。Lambda 底层用 invokedynamic 指令实现,不是生成匿名类。性能更好。
"为什么 Lambda 里的 this 指向外部类?" 因为 Lambda 没有自己的作用域——它和包围它的代码共享同一个 this。匿名内部类是一个独立的类,它的 this 就是它自己的实例。
"Stream 和集合有什么区别?"
- 集合存储数据,Stream 不存储——它只是数据的"视图"
- 集合可以被多次遍历,Stream 只能消费一次
- Stream 是懒执行的——中间操作只在终端操作触发时才真正计算
旅人笔记
Lambda 把行为装进信封传给别人。 方法引用让 Lambda 再短一步。 Stream 流水线 = filter → map → sorted → collect。 四个核心:Supplier 生产值,Consumer 消费值,Function 转换值,Predicate 判断值。
→ 下一站预告
你已经在"变量村"学完了编程的骨架知识——变量、控制流、方法、类、继承、Lambda。
第9章你会学到最常用的工具类:字符串怎么就不可变了?为什么不能直接拼 + 来造长字符串?枚举和 record 为什么能省那么多代码?这是你在新手村的最后一站——之后就要走进算法森林。