🚀 Java 巩固进阶 · 第 25 天

主题:Lambda 表达式 —— 函数式编程入门

📅 进度概览:恭喜完成 Java 基础巩固阶段(1-24 天)!从今天起,进入 JDK8+ 新特性阶段。Lambda 是 Java 函数式编程的基石,也是后续学习 Stream 流、Optional 乃至 Spring 函数式接口的 prerequisite。

💡 核心价值

  • 代码简洁:消除匿名内部类的样板代码,让核心逻辑更突出。
  • 函数式思维:将“行为”作为参数传递,为集合流式处理打下基础。
  • 框架基石:Spring 回调、MyBatis 函数式接口、Java 并发包都大量使用 Lambda。
  • 面试高频:函数式接口、方法引用、Lambda 底层原理是必考题。

一、为什么需要 Lambda?告别匿名内部类 🎯

1. 传统写法 vs Lambda 写法

// ❌ 传统:匿名内部类(啰嗦,重点不突出)
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("线程执行了");
    }
}).start();

// ✅ Lambda:只关心“做什么”,不关心“类结构”
new Thread(() -> {
    System.out.println("线程执行了");
}).start();

2. 本质理解

┌─────────────────────────────────────┐
│  🔄 Lambda 本质                      │
│  是“匿名函数” + “函数式接口实例”     │
│                                      │
│  前提:接口必须只有一个抽象方法      │
│  好处:编译器自动推断类型,代码极简  │
└─────────────────────────────────────┘

💡 一句话理解
“Lambda 就是简化版的匿名内部类,只保留核心逻辑代码”


二、语法规范与函数式接口 📝

1. Lambda 表达式语法

// 标准格式:(参数列表) -> { 方法体 }
(a, b) -> { return a + b; };

// ✅ 简化规则(编译器自动推断):
// 1. 参数类型可省略
// 2. 单个参数可省略括号
// 3. 单行代码可省略大括号和 return
(a, b) -> a + b;  // 最简写法

2. 函数式接口(@FunctionalInterface)

// ✅ 规范:只能有一个抽象方法
@FunctionalInterface
public interface MyCalculator {
    int calculate(int a, int b);  // 抽象方法
    
    // 可以有默认方法/静态方法,不影响函数式特性
    default void print() { System.out.println("计算"); }
}

// ✅ 使用 Lambda 实现
MyCalculator add = (a, b) -> a + b;
System.out.println(add.calculate(3, 5));  // 输出 8

3. JDK 内置四大核心函数式接口(⭐ 必背)

接口 泛型 抽象方法 作用 示例
Consumer <T> void accept(T t) 消费型(有参无返) list.forEach(System.out::println)
Supplier <T> T get() 供给型(无参有返) () -> new User()
Function <T,R> R apply(T t) 函数型(有参有返) s -> s.length()
Predicate <T> boolean test(T t) 断言型(判断) s -> s.isEmpty()

三、方法引用:Lambda 的极致简化 :: ⚡

1. 什么是方法引用?

🎯 场景:Lambda 体内部只是调用了一个已有方法
✅ 解决:直接用方法名引用,更简洁

2. 四种引用方式

// 1️⃣ 静态方法引用:类名::方法名
Function<String, Integer> parser = Integer::parseInt;  // 等价于 s -> Integer.parseInt(s)

// 2️⃣ 实例方法引用:对象::方法名
String str = "hello";
Supplier<Integer> lenProvider = str::length;  // 等价于 () -> str.length()

// 3️⃣ 类实例方法引用:类名::方法名(特殊,第一个参数作为调用者)
Function<String, Integer> lenFunc = String::length;  // 等价于 s -> s.length()

// 4️⃣ 构造器引用:类名::new
Supplier<User> userSupplier = User::new;  // 等价于 () -> new User()

💡 记忆口诀
“Lambda 体里只调用,方法引用来简化;静态类名实例对象,构造器用 new 替代”


四、🎯 今日实战任务:Lambda 重构练习

任务 1:重写线程创建代码

/**
 * 要求:
 * 1. 用 Lambda 表达式重写 Runnable 实现
 * 2. 用方法引用重写(如果适用)
 * 3. 对比代码行数差异
 * 
 * 💡 提示:
 * new Thread(() -> System.out.println("Hello"));
 */

任务 2:集合排序重构

/**
 * 业务场景:用户列表按年龄排序
 * 
 * 要求:
 * 1. 创建 User 类(name, age)
 * 2. 传统写法:new Comparator<User>() { ... }
 * 3. Lambda 写法:(u1, u2) -> u1.getAge() - u2.getAge()
 * 4. 方法引用:Comparator.comparingInt(User::getAge)
 * 
 * 💡 挑战:
 * - 尝试按姓名长度排序
 * - 尝试链式排序:先按年龄,年龄相同按姓名
 */
List<User> users = Arrays.asList(
    new User("Alice", 20),
    new User("Bob", 18)
);
// TODO: 用 Lambda 排序

任务 3:自定义函数式接口

/**
 * 要求:
 * 1. 定义 @FunctionalInterface Operation
 * 2. 抽象方法:int compute(int a, int b)
 * 3. 创建 Lambda 实现:加法、减法、乘法
 * 4. 编写方法 executeOperation(int a, int b, Operation op)
 * 
 * 💡 提示:
 * 模拟策略模式,用 Lambda 替代策略类
 */

任务 4:遍历与消费(Consumer)

/**
 * 要求:
 * 1. 创建 List<String> 包含 5 个名字
 * 2. 用 foreach 循环遍历打印
 * 3. 用 List.forEach() + Lambda 遍历打印
 * 4. 用方法引用 System.out::println 遍历打印
 * 
 * 💡 观察:
 * 代码越来越简洁,但可读性是否下降?(思考平衡点)
 */

📝 第 25 天 · 核心总结(极简背诵版)

  1. Lambda 本质

    简化版匿名内部类
    前提:函数式接口(只有一个抽象方法)
    
  2. 语法简化规则

    (int a, int b) -> { return a + b; }  // 标准
    (a, b) -> a + b                      // 最简
    
  3. 四大核心接口

    Consumer<T>:void accept(T)  → 消费
    Supplier<T>:T get()         → 供给
    Function<T,R>:R apply(T)    → 转换
    Predicate<T>:boolean test(T)→ 判断
    
  4. 方法引用

    静态方法:Class::method
    实例方法:object::method
    构造器:Class::new
    

明天预告:🔥 Stream 流式编程 —— 集合处理的革命!

  • Stream 创建(Collection.stream, Stream.of)
  • 中间操作(filter, map, sorted, distinct)
  • 终止操作(collect, forEach, count)
  • 实战:用户数据筛选、转换、统计

准备好了吗?明天我们让集合操作“流”起来! 🌊✨

更多推荐