讲解

Lambda 表达式(JDK 8 引入)让「把一小段行为当参数传来传去」变得轻量。它的语法是 (参数) -> { 方法体 },本质上是一个「函数式接口」(只含一个抽象方法的接口,如 Runnable、Comparator)的匿名实现。以前给 sort 传比较器要写一个匿名内部类七八行,现在一行 (a, b) -> a - b 搞定。参数类型可以省略让编译器推断,单参数时括号也能省,方法体只有一条语句时花括号和 return 都能省。

方法引用是 Lambda 的进一步简写:System.out::println 等价于 s -> System.out.println(s),Integer::sum 等价于 (a, b) -> a + b。看到双冒号就默念「把这个方法原样传进去」。要注意 Lambda 里使用外部局部变量时,该变量必须是 final 或「事实 final」(赋值后没再改过),这是编译器的硬性要求。

Stream API 建立在 Lambda 之上,为集合提供「声明式」的数据处理流水线:list.stream().filter(...).map(...).collect(...) 读作「过滤、变换、收集」,你只描述每一步要什么,循环细节被库接管。常用操作:filter(条件) 留下符合条件的、map(转换) 逐个变换、sorted() 排序、limit(n) 截取、forEach 逐个消费、collect(Collectors.toList()) 收拢成新列表、reduce 聚合成单值。Stream 不改原集合,每个中间操作都返回新流;写链式调用时一个操作一行,可读性最好。

示例

Lambda 替代匿名内部类:Runnable 与自定义排序:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> System.out.println("任务执行了");
        task.run();

        List<String> names = new ArrayList<>(List.of("张三", "李四四", "王五五五"));
        names.sort((a, b) -> a.length() - b.length());
        System.out.println("按长度排序:" + names);
        names.forEach(name -> System.out.println("你好," + name));
    }
}

Stream 流水线:过滤、变换、排序、收集,输出是确定性的:

import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Integer> scores = List.of(88, 59, 72, 45, 91, 60);

        List<Integer> passed = scores.stream()
            .filter(s -> s >= 60)
            .sorted()
            .collect(Collectors.toList());
        System.out.println("及格且升序:" + passed);

        List<String> labels = scores.stream()
            .map(s -> s >= 60 ? "及格(" + s + ")" : "不及格(" + s + ")")
            .collect(Collectors.toList());
        System.out.println("标签:" + labels);

        int total = scores.stream().reduce(0, Integer::sum);
        System.out.println("总分:" + total);
    }
}

常见坑

  • Lambda 里改外部局部变量:捕获的局部变量必须事实 final,int i = 0; 之后在 lambda 里 i++ 直接编译错误。需要累计就用数组、AtomicInteger 或 Stream 自带的 reduce。
  • Stream 用过一次再用:流只能消费一次,同一个 Stream 对象调完 filter 链再调 forEach 会抛 IllegalStateException。每次处理都重新 list.stream()。
  • 在 forEach 里 break/continue:Lambda 里的 return 只是结束本次回调,实现不了循环的 break。需要提前退出就别用 forEach,用普通循环或 filter 先把范围缩好。
  • 为了用 Stream 而用 Stream:三步以内的简单遍历,增强 for 往往更直白。Stream 的价值在多步流水线(过滤+变换+聚合),一行能写完的别硬凹。

小结

Lambda 是函数式接口的轻便实现,方法引用再简一层;Stream 用 filter/map/sorted/collect 搭声明式流水线,不改原集合。下一节学习程序与外部世界的桥梁:文件读写。