1. 概述

回调函数是在另一个函数中作为参数传递并在该函数完成或发生某个事件时执行的函数。在大多数编程语言中,当处理异步代码时,回调函数特别有用。

在这篇文章中,我们将学习Java中回调函数的实际用例以及如何实现它们。

2. 实现回调函数

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();
    }
}

SynchronousEventConsumer类有一个EventListener属性,它通过构造函数初始化。当调用doSynchronousOperation()方法时,它会返回从EventListeneronTrigger()方法获取的值:

EventListener listener = new SynchronousEventListenerImpl();
SynchronousEventConsumer synchronousEventConsumer = new SynchronousEventConsumer(listener);
String result = synchronousEventConsumer.doSynchronousOperation();

assertNotNull(result);
assertEquals("Synchronously running callback function", result);

让我们编写一个测试来演示doSynchronousOperation()方法如何调用listener变量的onTrigger()方法并获取其返回值:

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()方法中返回一个字符串字面量。

接下来,我们声明一个类,它异步地运行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()方法,它隐式地在一个新线程中调用EventListeneronTrigger()方法。

请注意,为每个方法调用创建新线程的方法是一个反模式,这里只是为了演示目的。生产级代码应该依赖于适当大小和调整过的线程池。有关更多关于Java并发的知识,请参阅我们的其他文章,如Java并发

让我们验证程序确实可以在doAsynchronousOperation()方法中调用onTrigger()方法:

2.3. 使用消费者

消费者是Java中常用的功能接口,通常用于函数式编程。接口的实现接受一个参数并使用提供的参数进行操作,但不返回结果。

通过使用消费者,我们可以将一个方法作为参数传递给另一个方法。这允许我们在父方法内部调用并运行内层方法的操作

考虑一个方法,它增加表示年龄的给定数字。我们可以将初始年龄作为第一个参数,并传递一个Consumer作为第二个方法来递增年龄。

以下是使用消费者作为回调函数实现这个功能的一个示例:

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变量的值等于initialAgeageDifference之和。

在这个上下文中,这些回调函数是传递给getAge()increaseAge()方法的函数。这些函数在每个getAge()increaseAge()方法完成其任务后被触发,执行任何自定义操作。

3. 总结

在这篇文章中,我们了解了Java中回调函数的概念。我们演示了如何通过接口同步和异步实现回调函数。我们还学习了如何在Java中使用Consumer功能接口进行回调操作。

本文提供的代码片段可在GitHub上找到。