跳到内容

元数据卡

  • 前置知识:第5章(方法定义)、第8章(接口)
  • 预计时间:20 分钟
  • 阅读模式:高度专注
  • 完成标志:能写出 Lambda 表达式,理解函数式接口

你的进度

老陈见你继承多态都用利索了,丢给你一个更刁钻的问题。村口需要一套事件报警系统——每次有状况都要执行一串操作,你不想为每种状况都新建一个类。

你现在的做法是每来一个新事件就创建一个新类:

java
class FloodEventHandler implements EventHandler {
    @Override
    public void handle(Event event) {
        notifyVillageChief(event);
        logEvent(event);
        prepareSandbags(event);
    }
}

三个事件 → 三个类。五个事件 → 五个类。

老陈师傅摇了摇头:"你就是造打铁炉子的——一锤一锤地敲。知不知道有一种更快的方法,可以把一段行为像包裹一样直接递过去?"

他拿出一片羽毛,在上面写了几行字。"这叫 Lambda。"

你的任务:能不能把"一段行为"当成一个值——像传数字、传字符串一样——直接传过去?


第一幕:函数式接口——Lambda 的插座

Lambda 需要一个"插座"——函数式接口,也就是只有一个抽象方法的接口

java
@FunctionalInterface // 安全网
public interface EventHandler {
    void handle(Event event);
}

标准库里的 RunnableCallableComparator——都只有一个抽象方法,等着被 Lambda 插上。


第二幕:Lambda——把行为装进信封

以前怎么用匿名内部类?

java
EventHandler handler = new EventHandler() {
    @Override
    public void handle(Event event) {
        System.out.println("事件发生: " + event.name());
    }
};

Lambda 把它压缩成一行:

java
EventHandler handler = (event) -> System.out.println("事件发生: " + event.name());

语言:Java 8+ 如何运行

java
public class LambdaDemo {
    public static void main(String[] args) {
        EventHandler h = (event) -> {
            System.out.println("事件: " + event.name());
            System.out.println("时间: " + event.time());
        };
        h.handle(new Event("敌军来袭", 1719123456789L));
        processEvent(e -> System.out.println("处理: " + e.name()));
    }
    static void processEvent(EventHandler h) {
        h.handle(new Event("深夜异动", 1719123456790L));
    }
}
record Event(String name, long time) {}

预期输出

事件: 敌军来袭
时间: 1719123456789
处理: 深夜异动

Lambda 语法拆解(参数列表) -> { 方法体 }

部分说明简化规则
(event)参数单一参数可省略括号
->Lambda 操作符固定写法
{ ... }方法体单语句可省花括号和 return

各种形式:

java
Runnable r = () -> System.out.println("跑起来了");            // 无参
Consumer<String> c = msg -> System.out.println(msg);          // 单参
Comparator<Integer> comp = (a, b) -> a - b;                   // 多参
Comparator<Integer> v = (a, b) -> {                           // 多行
    System.out.println("比较中...");
    return a - b;
};

第三幕:四大函数式接口

记住 四大天王

接口方法签名用途示意
Supplier<T>T get()生产值() -> "hello"
Consumer<T>void accept(T)消费值s -> System.out.println(s)
Function<T,R>R apply(T)转换值s -> s.length()
Predicate<T>boolean test(T)判断真假s -> s.length() > 5

真实例子:

java
List<String> msgs = List.of("敌军", "天气", "喜报", "求救");

Predicate<String> isUrgent = msg -> msg.equals("敌军") || msg.equals("求救");
Function<String, String> wrap = msg -> "[消息] " + msg;

msgs.stream().filter(isUrgent).map(wrap).forEach(System.out::println);

Supplier<String> ready = () -> "系统就绪";
System.out.println(ready.get());

语言:Java 8+ 预期输出 [消息] 敌军 [消息] 求救 系统就绪


常见陷阱:effectively final

java
String prefix = "[消息]";
EventHandler h = event -> {
    prefix = "[紧急] " + prefix; // 编译错误——修改了捕获变量
};

用数组包装来解决:

java
String[] prefix = {"[消息]"};
EventHandler h = event -> {
    prefix[0] = "[紧急]";
    System.out.println(prefix[0] + event.name());
};

旅人笔记

Lambda 把行为装进信封传给别人。 函数式接口是插座,Lambda 是插头。 四大天王:Supplier 生产值,Consumer 消费值,Function 转换值,Predicate 判断值。

下一站预告

Lambda 让你有了传递行为的工具。但还需要一个接受它的数据管道——Stream API。

Built with VitePress | Software Systems Atlas