Java 中的 Lambda 表达式详解:从语法糖到函数式编程


如果你还在写这样的代码:

Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.length() - o2.length();
    }
});

那么你可能已经落后于 Java 8 时代。

使用 Lambda 之后:

list.sort((a, b) -> a.length() - b.length());

代码不仅更简洁,而且表达力更强。

那么问题来了:

  • Lambda 到底是什么?
  • 为什么只能用于函数式接口?
  • Lambda 和匿名内部类有什么区别?
  • 它的底层原理是什么?
  • 什么时候应该用,什么时候不该用?

这篇文章带你系统掌握 Java Lambda。


一、什么是 Lambda 表达式?

Lambda 表达式是 Java 8 引入的函数式编程特性。

它的本质是:

用更简洁的语法来表示一个“函数式接口”的实现。


二、为什么会有 Lambda?

在 Java 8 之前,如果我们想传递“行为”,只能:

  • 使用匿名内部类
  • 写很多模板代码

例如:

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

使用 Lambda:

Runnable r = () -> System.out.println("Hello");

简洁程度完全不同。


三、Lambda 的基本语法

基本结构:

(parameters) -> expression

或:

(parameters) -> { statements }

1️⃣ 无参数

() -> System.out.println("Hello")

2️⃣ 单参数(可省略括号)

x -> x * 2

3️⃣ 多参数

(a, b) -> a + b

4️⃣ 带代码块

(a, b) -> {
    int result = a + b;
    return result;
}

⚠️ 有大括号必须写 return。


四、函数式接口(Lambda 的前提)

Lambda 只能用于:

只有一个抽象方法的接口

称为:函数式接口


示例

@FunctionalInterface
interface MyFunction {
    int add(int a, int b);
}

使用 Lambda:

MyFunction f = (a, b) -> a + b;

常见函数式接口

Java 8 提供了很多现成接口:

1️⃣ Runnable

() -> System.out.println("run")

2️⃣ Comparator

(a, b) -> a.length() - b.length()

3️⃣ Consumer

x -> System.out.println(x)

4️⃣ Supplier

() -> "hello"

5️⃣ Function<T, R>

x -> x.length()

6️⃣ Predicate

x -> x > 10

五、Lambda 的典型使用场景


场景 1:集合排序

list.sort((a, b) -> a.compareTo(b));

场景 2:遍历集合

list.forEach(x -> System.out.println(x));

场景 3:Stream 流操作

list.stream()
    .filter(x -> x > 5)
    .map(x -> x * 2)
    .forEach(System.out::println);

场景 4:线程创建

new Thread(() -> System.out.println("Thread running")).start();

六、方法引用(Lambda 的进阶写法)

当 Lambda 只是调用一个方法时,可以简化:

x -> System.out.println(x);

可以写成:

System.out::println

常见形式:

类型示例
静态方法Class::staticMethod
实例方法object::method
构造方法Class::new

示例:

list.forEach(System.out::println);

七、Lambda 与匿名内部类的区别

对比Lambda匿名内部类
语法简洁冗长
this 指向外部类当前匿名类
是否生成 class 文件否(动态生成)
性能更优稍慢

八、Lambda 底层原理

很多人以为 Lambda 是语法糖。

实际上:

Java 使用 invokedynamic 指令 + LambdaMetafactory 实现。

特点:

  • 不会生成额外 class 文件
  • 运行时动态创建函数对象
  • 性能优于匿名内部类

九、易错点总结


❌ 1. 不是所有接口都能用 Lambda

必须是:

@FunctionalInterface

❌ 2. 变量必须是“有效 final”

int x = 10;
list.forEach(i -> System.out.println(x));

如果修改 x 会报错。


❌ 3. Lambda 不适合复杂逻辑

如果代码超过 5-6 行:

建议写成普通方法。


❌ 4. 可读性问题

过度嵌套:

list.stream().filter(...).map(...).flatMap(...).collect(...)

维护困难。


十、Lambda 的优势

  1. 代码简洁
  2. 提高可读性
  3. 支持函数式编程
  4. 与 Stream 深度结合
  5. 支持并行计算

十一、什么时候不建议用 Lambda?

  • 复杂业务逻辑
  • 需要多步异常处理
  • 代码可读性下降时
  • 调试困难场景

十二、总结

Lambda 的本质是:

让“行为”成为一等公民。

它的价值不仅在于:

  • 少写代码

更在于:

  • 推动 Java 进入函数式编程时代
  • 与 Stream API 深度结合
  • 提升并发与集合处理能力

真正掌握 Lambda,你需要理解:

  • 函数式接口
  • 方法引用
  • Stream 操作
  • invokedynamic 原理
  • 变量捕获机制

更多推荐