Java 中的回调函数
上次更新:2024 年 1 月 8 日
1. 概述
回调函数是指作为参数传递给另一个函数,并在该函数完成或发生某个事件时执行的函数。在大多数编程语言中,回调函数在使用异步代码时特别有用。
在本文中,我们将学习回调函数在 Java 中的实际用例以及如何实现它们。
2. 实现回调函数
通常,我们可以在 Java 中通过暴露一个接口并接受其实现作为参数来创建回调函数。这样的回调可以同步或异步调用。
2.1. 同步回调
同步操作是指一个任务需要在另一个任务开始之前完成的操作。.
例如,想象一下这样的接口
public interface EventListener {
String onTrigger();
}
上面的代码片段声明了一个EventListener接口,其中包含一个onTrigger()方法,该方法具有一个String返回类型。这将是我们的回调。
接下来,让我们声明一个实现该接口的具体类
public class SynchronousEventListenerImpl implements EventListener {
@Override
public String onTrigger(){
return "Synchronously running callback function";
}
}
SynchronousEventListenerImpl类实现了上面所示的EventListener接口。
接下来,让我们创建一个SynchronousEventConsumer类,它组合EventListener接口的一个实例并调用其onTrigger()方法
public class SynchronousEventConsumer {
private final EventListener eventListener;
// constructor
public String doSynchronousOperation(){
System.out.println("Performing callback before synchronous Task");
// any other custom operations
return eventListener.onTrigger();
}
}
SyncronousEventConsumer类具有一个EventListener属性,它通过构造函数进行初始化。当调用doSynchronousOperation()方法时,它会返回从EventListener的onTrigger()方法获得的值。
让我们编写一个测试来演示doSynchronousOperation()方法调用侦听器变量的onTrigger()方法并获取其返回值
EventListener listener = new SynchronousEventListenerImpl();
SynchronousEventConsumer synchronousEventConsumer = new SynchronousEventConsumer(listener);
String result = synchronousEventConsumer.doSynchronousOperation();
assertNotNull(result);
assertEquals("Synchronously running callback function", result);
2.2. 异步回调函数
异步操作是指并行运行的操作。与上一节中说明的同步操作不同,异步任务是非阻塞的。它们在执行操作之前不会互相等待。让我们更新EventListener接口,以说明 Java 中的异步回调函数
public interface EventListener {
String onTrigger();
void respondToTrigger();
}
接下来,让我们为修改后的EventListener创建实现
public class AsynchronousEventListenerImpl implements EventListener {
@Override
public String onTrigger(){
respondToTrigger();
return "Asynchronously running callback function";
}
@Override
public void respondToTrigger(){
System.out.println("This is a side effect of the asynchronous trigger.");
}
}
上面的类实现了我们在上一节中声明的EventListener接口,并在其重写的onTrigger()方法中返回一个String字面量。
接下来,我们声明一个异步运行onTrigger()方法的类作为回调函数
public class AsynchronousEventConsumer{
private EventListener listener;
public AsynchronousEventConsumer(EventListener listener) {
this.listener = listener;
}
public void doAsynchronousOperation(){
System.out.println("Performing operation in Asynchronous Task");
new Thread(() -> listener.onTrigger()).start();
}
}
上面的AsynchronousEventConsumer类声明了一个doAsynchronousOperation()方法,该方法隐式地在一个新线程中调用EventListener的onTrigger()方法。
请注意,为每个方法调用创建一个新的Thread是一种反模式,此处仅用于演示目的。生产就绪的代码应依赖于适当大小和调整的线程池。 请查看我们其他的文章,以了解更多关于Java 并发的信息。
让我们验证程序是否确实从doAsynchronousOperation()方法中调用了onTrigger()方法
EventListener listener = Mockito.mock(AsynchronousEventListenerImpl.class);
AsynchronousEventConsumer synchronousEventListenerConsumer = new AsynchronousEventConsumer(listener);
synchronousEventListenerConsumer.doAsynchronousOperation();
verify(listener, timeout(1000).times(1)).onTrigger();
2.3. 使用 Consumers
消费者(Consumers)是函数式接口,常用于 Java 中的函数式编程。该接口的实现接受一个参数并对提供的参数执行操作,但不返回结果。
使用消费者,我们可以将一个方法作为参数传递给另一个方法。这允许我们在父方法内部调用并运行内部方法的操作。.
让我们考虑一个方法,该方法增加表示年龄的给定数字的值。我们可以将初始年龄作为第一个参数传递,并将一个消费者作为第二个方法来增加年龄。
以下说明了我们如何使用消费者将此作为回调函数实现
public class ConsumerCallback {
public void getAge(int initialAge, Consumer<Integer> callback) {
callback.accept(initialAge);
}
public void increaseAge(int initialAge, int ageDifference, Consumer<Integer> callback) {
System.out.println("===== Increase age ====");
int newAge = initialAge + ageDifference;
callback.accept(newAge);
}
}
在上面的getAge() 方法中,我们将initialAge 变量作为参数传递给callback.accept() 方法。accept() 方法接受一个参数(在本例中,是一个整数),然后通过传递给getAge() 方法作为参数的运行时方法或函数对输入执行任何操作。
increaseAge() 方法将在initialAge 变量上执行递增操作。它将initialAge 的值添加到ageDifference,然后将结果传递给第三个参数(Consumer)的accept() 方法。
以下是上述实现的演示
ConsumerCallback consumerCallback = new ConsumerCallback();
int ageDifference = 10;
AtomicInteger newAge1 = new AtomicInteger();
int initialAge = 20;
consumerCallback.getAge(initialAge, (initialAge1) -> {
consumerCallback.increaseAge(initialAge, ageDifference, (newAge) -> {
System.out.printf("New age ==> %s", newAge);
newAge1.set(newAge);
});
});
assertEquals(initialAge + ageDifference, newAge1.get());
在上面的代码片段中,我们将一个函数传递给getAge() 方法。此函数调用increaseAge() 方法并断言newAge 变量的值等于initialAge 和ageDifference 的总和。
在此上下文中,回调函数是传递给getAge() 和increaseAge() 方法的函数。这些函数被触发以在getAge() 和increaseAge() 方法完成其任务后执行任何自定义操作。
3. 结论
在本文中,我们了解了 Java 中的回调函数概念。我们演示了如何通过接口同步和异步地实现回调函数。我们还学习了如何使用 Java Consumer 函数式接口在 Java 中执行回调操作。
支持本文的代码可在 GitHub 上获取。 一旦你以 Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。















