快速上手:Lambda表达式与回调函数

一、Lambda 表达式详解

1. 什么是 Lambda 表达式?

一句话概括:Lambda 表达式是一个可以传递的匿名函数

  • 匿名:它没有像普通方法那样的明确名称。
  • 函数:它像方法一样有参数列表、函数体和返回类型,但它不属于任何特定的类。
  • 可传递:你可以像传递一个变量一样,将 Lambda 表达式作为参数传递给方法,或者从方法中返回。

它的核心作用是简化代码,特别是在使用只有一个抽象方法的接口(即函数式接口)时。

2. Lambda 表达式的语法结构

Lambda 表达式的基本语法非常简洁,通常表现为:

(parameters) -> expression

或者

(parameters) -> { statements; }
  • (parameters): 参数列表。
    • 如果没有参数,就用空括号 () 表示。
    • 如果只有一个参数,括号可以省略,例如 param -> ...
    • 参数类型通常可以省略,因为编译器可以通过上下文推断出来(类型推断)。
  • ->箭头操作符或 ** Lambda 操作符 **。它将参数列表和函数体分开。
  • expression 或 { statements; }: 函数体。
    • 表达式体 (expression): 当函数体只有一条语句时,可以省略花括号 {} 和 return 关键字。表达式的结果会自动作为返回值。
    • 语句块体 ({ statements; }): 当函数体包含多条语句时,必须使用花括号 {} 包裹。如果有返回值,必须显式使用 return 关键字。
3. Lambda 表达式的使用前提:函数式接口

Lambda 表达式并不能在任何地方使用。它只能被赋值给一个函数式接口类型的变量,或者作为参数传递给一个接受函数式接口作为参数的方法。

函数式接口:是指只包含一个抽象方法的接口。

  • 你可以使用 @FunnalInterface 注解来明确标识一个接口是函数式接口,编译器会帮你检查。
  • Java 8 内置了大量常用的函数式接口,都在 java.util.function 包下。

示例:自定义一个函数式接口

@FunctionalInterface
public interface MyCalculator {
    int calculate(int a, int b);
}

现在,我们可以用 Lambda 表达式来创建 MyCalculator 的实例:

public class LambdaExample {
    public static void main(String[] args) {
        // 使用 Lambda 表达式实现加法
        MyCalculator adder = (a, b) -> a + b;
        System.out.println("10 + 5 = " + adder.calculate(10, 5)); // 输出: 10 + 5 = 15

        // 使用 Lambda 表达式实现乘法
        MyCalculator multiplier = (a, b) -> {
            System.out.println("正在计算乘法...");
            return a * b;
        };
        System.out.println("10 * 5 = " + multiplier.calculate(10, 5)); // 输出: 正在计算乘法... \n 10 * 5 = 50
    }
}

二、常用的 Lambda 表达式示例

以下是一些结合 Java Stream API 和 java.util.function 包中常用函数式接口的 Lambda 表达式示例。

1. Consumer<T> (消费者)

接收一个参数,没有返回值。常用于 forEach 方法。

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 打印列表中的每个元素
names.forEach(name -> System.out.println(name));

// 使用方法引用(Method Reference)可以更简洁
names.forEach(System.out::println);
2. Predicate<T> (谓词)

接收一个参数,返回一个布尔值。常用于 filter 方法。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

// 过滤出所有偶数
List<Integer> evenNumbers = numbers.stream()
                                   .filter(n -> n % 2 == 0)
                                   .collect(Collectors.toList());
// evenNumbers 结果: [2, 4, 6]
3. Function<T, R> (函数)

接收一个类型为 T 的参数,返回一个类型为 R 的结果。常用于 map 方法。

List<String> words = Arrays.asList("apple", "banana", "cherry");

// 将每个字符串映射为其长度
List<Integer> wordLengths = words.stream()
                                 .map(word -> word.length())
                                 .collect(Collectors.toList());
// wordLengths 结果: [5, 6, 6]
4. Supplier<T> (供应商)

不接收任何参数,返回一个类型为 T 的结果。常用于 supplyAsync 方法。

// 异步生成一个随机数
CompletableFuture<Integer> randomFuture = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(1000); // 模拟耗时操作
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return new Random().nextInt(100);
});
5. Comparator<T> (比较器)

用于定义对象的排序规则。

List<String> fruits = Arrays.asList("Pineapple", "Apple", "Banana");

// 按字符串长度升序排序
fruits.sort((f1, f2) -> f1.length() - f2.length());
// fruits 结果: [Apple, Banana, Pineapple]

// 按字符串自然顺序(字典序)降序排序
fruits.sort((f1, f2) -> f2.compareTo(f1));
// fruits 结果: [Pineapple, Banana, Apple]

三、回调函数详解

1. 什么是回调函数?

一句话概括:回调函数是一个通过函数参数传递到另一个函数中,并在那个函数内部被调用的函数。

你可以把它想象成一个 “回头再打给你” 的场景:

  1.  (调用者) 给朋友 (被调用者) 打电话,提出一个请求(比如:“帮我查一下明天的天气”)。
  2.  告诉朋友:“查好了打我这个电话(你的电话号码)告诉我结果。” 这里的 “你的电话号码” 就是回调地址
  3.  挂了电话,就可以去做别的事了(非阻塞)。
  4. 过了一会儿,朋友查好了天气,他主动拨打你的电话执行回调),告诉你天气情况。

在这个例子中,提供给朋友的那个 “可以回拨的电话号码”,在编程中就是回调函数

2. 回调函数的分类
  • 同步回调:被调用者在处理完请求后,立即调用回调函数,然后才返回。调用者会一直等待,直到回调执行完毕。这是一种 “阻塞” 的等待。
  • 异步回调:被调用者接收请求后,会在另一个线程中处理任务,然后立即返回,不会等待任务完成。当任务处理完毕后,再在那个新线程中调用回调函数来通知调用者。这是一种 “非阻塞” 的等待。
3. 回调函数的例子

示例 1:同步回调 (使用 Lambda 表达式)

我们创建一个 Notifier 类,它的 notify 方法接收一个消息和一个 Callback

// 1. 定义一个回调接口
@FunctionalInterface
interface Callback {
    void onComplete(String result);
}

// 2. 被调用者
class Notifier {
    public void notify(String message, Callback callback) {
        System.out.println("Notifier: 开始处理消息...");
        // 模拟耗时操作
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String result = "消息处理完毕: " + message;
        // 处理完成后,调用回调函数
        callback.onComplete(result); 
        System.out.println("Notifier: 已调用回调,任务结束。");
    }
}

// 3. 调用者
public class CallbackExample {
    public static void main(String[] args) {
        Notifier notifier = new Notifier();
        
        System.out.println("Main: 准备调用 notify 方法...");
        
        // 使用 Lambda 表达式作为回调函数
        notifier.notify("Hello Callback", (result) -> {
            System.out.println("Main: 收到回调通知,结果是: " + result);
        });
        
        System.out.println("Main: notify 方法调用返回,主线程继续执行。");
    }
}

执行结果:

plaintext

Main: 准备调用 notify 方法...
Notifier: 开始处理消息...
Main: 收到回调通知,结果是: 消息处理完毕: Hello Callback
Notifier: 已调用回调,任务结束。
Main: notify 方法调用返回,主线程继续执行。

分析:主线程调用 notifier.notify() 后,会一直等待,直到 notify 方法内部的 callback.onComplete() 执行完毕,notify 方法才返回。这就是同步回调

示例 2:异步回调 (使用 CompletableFuture)

CompletableFuture 是实现异步回调的绝佳例子。

public class AsyncCallbackExample {
    public static void main(String[] args) {
        System.out.println("Main: 开始异步任务...");

        CompletableFuture.supplyAsync(() -> {
            // 1. 这部分在另一个线程中执行 (类似“朋友”在查天气)
            System.out.println("Async Task: 开始执行耗时操作...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Async Task: 操作完成,准备返回结果。");
            return "任务成功完成!";
        }).thenAccept(result -> {
            // 2. 这是回调函数 (类似“朋友”打回电话)
            // 它会在 supplyAsync 的任务完成后,在同一个或另一个线程中自动执行
            System.out.println("Callback: 收到异步任务的结果: " + result);
        });

        System.out.println("Main: 异步任务已提交,主线程可以做其他事了。");
        
        // 等待一段时间,确保能看到回调的输出
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Main: 程序结束。");
    }
}

执行结果 (线程名称可能不同):

Main: 开始异步任务...
Main: 异步任务已提交,主线程可以做其他事了。
Async Task: 开始执行耗时操作...
Async Task: 操作完成,准备返回结果。
Callback: 收到异步任务的结果: 任务成功完成!
Main: 程序结束。

分析:主线程调用 supplyAsync 后,立即返回,继续执行下一行代码。而 supplyAsync 的任务在 ForkJoinPool 的线程中后台运行。当任务完成后,thenAccept 注册的回调函数被自动触发。这就是异步回调

总结

  • Lambda 表达式是一种语法糖,极大地简化了函数式接口的实例化。它让你能写出更紧凑、更易读的代码,尤其在处理集合和并发编程时。
  • 回调函数是一种设计模式,描述了 “我把一个函数给你,你在需要的时候调用它” 的交互方式。
  • Lambda 表达式和回调函数的关系:Lambda 表达式是实现回调函数的完美工具。在 Java 中,当你需要传递一个回调函数时,通常就是传递一个函数式接口的实例,而使用 Lambda 表达式可以非常方便地创建这个实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值