调用方式
同步调用:类A的方法a()调用类B的方法b(),一直等待b()方法执行完毕,a()方法继续往下走。这种调用方式适用于方法b()执行时间不长的情况,因为b()方法执行时间一长或者直接阻塞的话,a()方法的余下代码是无法执行下去的,这样会造成整个流程的阻塞。
异步调用:类A的方法方法a()通过新起线程的方式调用类B的方法b(),代码接着直接往下执行。
回调:包括同步回调和异步回调。
- 类A的a()方法调用类B的b()方法
- 类B的b()方法执行完毕主动调用类A的callback()方法
经典回调方式
Class A实现接口CallBack callback——背景1
class A中包含一个class B的引用b ——背景2
class B有一个参数为callback的方法f(CallBack callback) ——背景3
A的对象a调用B的方法 f(CallBack callback) ——A类调用B类的某个方法 C
然后b就可以在f(CallBack callback)方法中调用A的方法 ——B类调用A类的某个方法D
示例
以工人向老板请假为例:
同步回调:工人打电话向老板请假,在电话中一直等待老板回复,老板同意后,再答谢老板。
异步回调:工人打电话向老板请假,老板在开会等会给答复,然后工人挂掉电话干别的事情并等待老板的通知,老板回电话同意请假,工人答谢老板。
结构设计
Callback 接口中有 replyWorker() 答复工人请假请求接口,也是老板决定后需要回调的方法。
Worker 类实现回调接口,并持有 Boss 的引用,向 Boss 注册监听器,等待 Boss 的回复。
Boss 类回调 Worker 的方法来答复 Worker 的请假。
代码实现
Callback 接口
interface Callback {
/**
* 答复工人请假请求
*/
void replyWorker(String message);
}
Worker 类实现回调接口,并持有 Boss 的引用,向 Boss 注册监听器,等待 Boss 的回复
class Worker implements Callback {
private Boss boss;
public Worker(Boss boss){
this.boss = boss;
}
public void leave(){
System.out.println("请求批准请假两天");
//同步回调 (可以分别注释掉同步和异步回调的代码,看运行效果)
boss.reply(this); //工人注册监听方法
System.out.println("同步阻塞等待老板的答复后,才能做别的事情");
//异步回调
new Thread(new Runnable() {
@Override
public void run() {
boss.reply(Worker.this);
}
}).start();
System.out.println("异步非阻塞,做别的事情并等待老板的回复");
}
@Override
public void replyWorker(String message){
System.out.println("老板回复: " + message);
}
}
Boss 类回调 Worker 的方法来答复 Worker 的请假
class Boss {
public void reply(Callback callback){
try {
//老板思考2s 后回复
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
callback.replyWorker("同意请假");
}
}
测试代码
public class CallbackPattern {
public static void main(String[] args){
Boss boss = new Boss();
Worker worker = new Worker(boss);
worker.leave();
}
}
同步回调运行结果
请求批准请假两天
老板回复: 同意请假
同步阻塞等待老板的答复后,才能做别的事情
异步回调运行结果
请求批准请假两天
异步非阻塞,做别的事情并等待老板的回复
老板回复: 同意请假
应用
回调是一种机制,很多语言里都有不同的实现,观察者模式也是回调的一种具体实现。观察者模式可以见我的 观察者模式 文章。回调机制的应用也很广泛,例如说java.util.Collections.sort(List, Comparator),这个sort()方法就定义了一个流程实现排序,而具体的顺序则由传入的Comparator参数来确定——这就是一个同步回调。又例如说GUI编程中,一个按钮被点击之后要做点事情,大家可以注册个ActionListener上去监听点击事件,在点击时被调用,这就是一个异步回调。
总结
回调的核心就是回调方将自身的引用传递给调用方,这样调用方就可以在调用完毕之后告诉回调方它想要知道的信息。本文主要是我的思考和总结。