代码改变世界

Lambda - 教程

2025-10-03 13:58  tlnshuju  阅读(5)  评论(0)    收藏  举报

Lambda 表达式是 Java 8 引入的重要特性,本质是匿名函数(没有名称的函数),用于简化函数式接口的实现,使代码更简洁、紧凑。它特别适合处理集合的遍历、过滤、映射等操作,是函数式编程在 Java 中的核心体现。

一、Lambda 表达式的基本概念

1. 函数式接口(前提)

Lambda 表达式只能用于函数式接口—— 即只包含一个抽象方法的接口(可包含默认方法或静态方法)。Java 内置了大量函数式接口(如 RunnableComparatorConsumer 等),也可自定义。

// 自定义函数式接口(只有一个抽象方法)
@FunctionalInterface // 注解可校验是否为函数式接口
interface MathOperation {
    int operate(int a, int b);
}
2. Lambda 语法结构

Lambda 表达式的语法格式:

(参数列表) -> { 方法体 }
  • 参数列表:与函数式接口中抽象方法的参数一致,可省略参数类型(编译器自动推断)。
  • ->:箭头符号,分隔参数列表和方法体。
  • 方法体:实现抽象方法的逻辑,若只有一行代码可省略 {} 和 return(若有返回值)。

二、Lambda 表达式的简化写法

根据场景不同,Lambda 可逐步简化,以下是常见形式:

1. 完整写法(带参数类型和方法体)
MathOperation addition = (int a, int b) -> {
    return a + b;
};
2. 省略参数类型(编译器自动推断)
MathOperation subtraction = (a, b) -> {
    return a - b;
};
3. 方法体只有一行代码(省略 {} 和 return
MathOperation multiplication = (a, b) -> a * b; // 自动返回结果
4. 无参数的情况
// Runnable 接口(无参数,无返回值)
Runnable runnable = () -> System.out.println("Lambda 运行中");
5. 单个参数(可省略参数的括号)
// Consumer 接口(单个参数,无返回值)
Consumer consumer = s -> System.out.println(s);

三、Lambda 表达式的核心应用场景

1. 替代匿名内部类

传统匿名内部类代码冗长,Lambda 可大幅简化:

传统方式(匿名内部类)

// 线程创建
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("线程运行");
    }
}).start();

Lambda 方式

new Thread(() -> System.out.println("线程运行")).start();
2. 集合遍历与操作

结合 Java 8 新增的 Stream API,Lambda 可简洁处理集合的遍历、过滤、排序等:

示例 1:遍历集合

List list = Arrays.asList("apple", "banana", "cherry");
// 传统 for 循环
for (String s : list) {
    System.out.println(s);
}
// Lambda + forEach
list.forEach(s -> System.out.println(s));
// 更简化(方法引用)
list.forEach(System.out::println);

示例 2:集合排序

List numbers = Arrays.asList(3, 1, 2);
// 传统方式(匿名内部类)
Collections.sort(numbers, new Comparator() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 - o2; // 升序
    }
});
// Lambda 方式
Collections.sort(numbers, (a, b) -> a - b); // 升序
// 更简化(方法引用)
numbers.sort(Integer::compareTo);

示例 3:过滤与映射(Stream API)

List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 过滤偶数并计算平方和
int sum = numbers.stream()
    .filter(n -> n % 2 == 0) // 过滤偶数
    .map(n -> n * n) // 计算平方
    .reduce(0, Integer::sum); // 求和
System.out.println(sum); // 输出:2² + 4² + 6² = 4 + 16 + 36 = 56
3. 自定义函数式接口

通过自定义函数式接口,可将 Lambda 作为参数传递,实现灵活的业务逻辑:

// 自定义函数式接口(处理字符串)
@FunctionalInterface
interface StringProcessor {
    String process(String s);
}
// 工具方法:接收 Lambda 作为参数
public static String handleString(String s, StringProcessor processor) {
    return processor.process(s);
}
// 使用
public static void main(String[] args) {
    // 转换为大写
    String upper = handleString("hello", s -> s.toUpperCase());
    System.out.println(upper); // HELLO
    // 反转字符串
    String reversed = handleString("hello", s -> new StringBuilder(s).reverse().toString());
    System.out.println(reversed); // olleh
}

四、Lambda 表达式的底层原理

Lambda 表达式在编译后不会生成匿名内部类,而是通过 invokedynamic 指令动态生成一个函数式接口的实现类,本质是语法糖,但比匿名内部类更高效(减少类文件生成)。

例如,以下 Lambda 表达式:

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

编译后会生成一个类似以下的实现类(由 JVM 动态创建,不在字节码中显式存在):

class Lambda$1 implements Runnable {
    @Override
    public void run() {
        System.out.println("Lambda");
    }
}

五、Lambda 表达式的注意事项

  1. 访问外部变量:Lambda 可访问外部的 final 或事实上 final(即未声明为 final 但未被修改)的变量。

    int num = 10; // 事实上 final(未被修改)
    Runnable r = () -> System.out.println(num); // 合法
    // num = 20; // 若修改,编译报错
  2. this 关键字:Lambda 中的 this 指向包围它的外部类对象,而匿名内部类的 this 指向自身实例。

  3. 方法引用(进一步简化):若 Lambda 方法体只是调用一个已存在的方法,可使用方法引用:: 语法)进一步简化:

    // Lambda 方式
    list.forEach(s -> System.out.println(s));
    // 方法引用方式(等价)
    list.forEach(System.out::println);

    常见方法引用类型:

    • 静态方法引用:ClassName::staticMethod(如 Integer::parseInt)。
    • 实例方法引用:instance::method(如 str::length)。
    • 类的实例方法引用:ClassName::method(如 String::equals)。
    • 构造方法引用:ClassName::new(如 ArrayList::new)。

总结

Lambda 表达式的核心价值是简化代码支持函数式编程,尤其在集合操作和事件处理中优势明显。其使用前提是函数式接口,语法简洁灵活,可根据场景逐步简化。

掌握 Lambda 表达式需要理解:

  • 函数式接口是基础。
  • 语法格式:(参数) -> 方法体
  • 常见应用:替代匿名内部类、集合遍历与 Stream 操作。
  • 方法引用是 Lambda 的进一步简化。

Lambda 表达式是 Java 现代化的重要一步,与 Stream API、Optional 等特性结合,能显著提升代码的可读性和开发效率。