详解Java的回调机制
模块之间总是存在这一定的接口,从调用方式上看,可以分为三类:同步调用、回调和异步调用。下面着重详解回调机制。
1. 概述
java 中的回调机制是一个比较常见的机制,只是有可能在你的程序中使用得比较少,在一些大型的框架中回调机制随处可见。本文就通过一些具体的实例,慢慢走近 java 的回调机制。
2.回调
所谓回调:就是a类中调用b类中的某个方法c,然后b类中反过来调用a类中的方法d,d这个方法就叫回调方法。实际在使用的时候,也会有不同的回调形式,比如下面的这几种。
2.1 同步回调
这里我假设这样的一种情况。
a 公司的总监 b 跟他的下属(项目经理 c)说要做一个调研,不过不用 c 自己亲力亲为。可以让经理 c 去安排他下面的程序员 d 去完成。经理 c 找到了程序员 d,并告诉他,现在要完成一个调研任务。并且把调研的结果告诉经理 c。如果有问题,还是要继续的。 因为这里是 c 让 d 去做一件事情,之后 d 还是要将结果与 c 进行沟通。这样就是回调的模型了。下面是一般回调的类图:
首先我们要有一个回调的接口 callbackinterface
callbackinterface.java
public interface callbackinterface { public boolean check(int result); }
背景里,程序员 d 是要将结果与项目经理 c 进行沟通的,所以这里项目经理需要实现上面的回调接口:
manager.java
public class manager implements callbackinterface { private programmer programmer = null; public manager(programmer _programmer) { this.programmer = _programmer; } /** * 用于 boss 下达的委托 */ public void entrust() { arrange(); } // 进行安排下属进行 study 工作 private void arrange() { system.out.println("manager 正在为 programmer 安排工作"); programmer.study(manager.this); system.out.println("为 programmer 安排工作已经完成,manager 做其他的事情去了。"); } @override public boolean check(int result) { if (result == 5) { return true; } return false; } }
对于程序员 d 来说他需要持有一个经理 c 的引用,以便与他沟通。不过,这里是总监 b 让 经理 c 去安排的任务。也就是说这里也可以让其他的经理,比如说经理 b1, b2等等。因为经理都实现了回调的接口,所以这里就可以直接让程序员 d 持有这个接口就可以了。如下:
programmer.java
public class programmer { public void study(callbackinterface callback) { int result = 0; do { result++; system.out.println("第 " + result + " 次研究的结果"); } while (!callback.check(result)); system.out.println("调研任务结束"); } }
对于总监来说就更简单明了了,因为这相当于一个 client 测试:
boss.java
public class boss { public static void main(string[] args) { manager manager = new manager(new programmer()); manager.entrust(); } }
运行结果:
manager 正在为 programmer 安排工作
第 1 次研究的结果
第 2 次研究的结果
第 3 次研究的结果
第 4 次研究的结果
第 5 次研究的结果
调研任务结束
为 programmer 安排工作已经完成,manager 做其他的事情去了。
2.2 异步回调
还是上面的例子,你的项目经理不可能要一直等你调研的结果。而是把这个任务交给你之后,他就不管了,他做他的,你做你的。所以,这里需要对回调的函数进行异步处理。
所以,这里我们需要修改 programmer 类的代码,修改如下:
programmer.java
public class programmer { public programmer() { } public void study(callbackinterface callback) { new studythread(callback).start(); } // --------------------------- programmer 正在做的工作 --------------------------- class studythread extends thread { callbackinterface callback = null; public studythread(callbackinterface _callback) { callback = _callback; } @override public void run() { int result = 0; do { result++; system.out.println("第 " + result + " 次研究的结果"); } while (!callback.check(result)); system.out.println("调研任务结束"); } } }
运行结果:
manager 正在为 programmer 安排工作
为 programmer 安排工作已经完成,manager 做其他的事情去了。
第 1 次研究的结果
第 2 次研究的结果
第 3 次研究的结果
第 4 次研究的结果
第 5 次研究的结果
调研任务结束
2.3 闭包与回调
闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。
2.3.1 普通调用
首先,我们可以看看在正常情况下的调用是怎么进行的。
incrementable.java
interface incrementable { void increment(); }
这是一个普通的接口(在普通调用里只是普通接口,在回调中就是回调接口,这一点应该很好理解吧)。
callee1.java
class callee1 implements incrementable { private int i = 0; @override public void increment() { i++; system.out.println(i); } }
callbacks.java
public class callbacks { public static void main(string[] args) { callee1 callee1 = new callee1(); callee1.increment(); } }
callbacks 是一个测试客户端类,没啥好说的,直接看上面的代码。
2.3.2 回调初试
上面的普通调用也没啥好说的,因为这对于一个正常的 java 程序员来说都应该是想都不用想就可以搞定的事情。
现在如果要构成回调,那么对于程序的结构或是逻辑的思维上都不可能只有一个被调用者(被回调的对象 callee1),还需要一个调用者对象。调用者可以像下面这样来编写:
caller.java
class caller { private incrementable callbackreference; public caller(incrementable _callbackreference) { callbackreference = _callbackreference; } void go() { callbackreference.increment(); } }
这里 caller 持有一个回调接口的引用 callbackreference,就像在上面说到的程序员需要持有一个项目经理的引用,这样就可以通过这个引用来与项目经理沟通。这里的 callbackreference 也正是起到了这个作用。
现在我们来看看测试类的编写:
callbacks.java
public class callbacks { public static void main(string[] args) { callee1 callee1 = new callee1(); caller caller1 = new caller(callee1); caller1.go(); } }
对于到目前为止的程序代码,完全可以对比上面项目经理安排程序员调研技术难题的代码。有异曲同工之妙。
2.3.3 闭包回调
相比于正常的回调,闭包回调的核心自然是在于闭包,也就是对作用域的控制。
现在假设有一个用户(其他程序员)自定义了一个 myincrement 类,同时包含了一个 increment 的方法。如下:
class myincrement { public void increment() { system.out.println("mycrement.increment"); } static void f(myincrement increment) { increment.increment(); } }
另外有一个类 callee2 继承自上面这个类:
class callee2 extends myincrement { private int i = 0; public void increment() { super.increment(); i++; system.out.println(i); } }
显而易见这里如果要调用 increment() 方法,就变成了一般的函数调用了。所以这里我们需要修改上面的 callee2 类,修改的目标就是让 callee2 类可以兼容 myincrement 类的 increment() 方法和 incrementable 的 increment() 方法。修改后:
class callee2 extends myincrement { private int i = 0; public void increment() { super.increment(); i++; system.out.println(i); } private class closure implements incrementable { @override public void increment() { callee2.this.increment(); } } incrementable getcallbackreference() { return new closure(); } }
注意,这里的 closure 类是一个私有的类,这是一个闭包的要素。因为 closure 类是私有的,那么就要有一个对外开放的接口,用来对 closure 对象的操作,这里就是上面的 getcallbackreference() 方法。 caller 类则没有改变。
对于测试客户端就直接看代码吧:
public class callbacks { public static void main(string[] args) { callee2 callee2 = new callee2(); caller caller2 = new caller(callee2.getcallbackreference()); caller2.go(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。