Java接口回调
1. 关于回调
回调机制是一种常见的设计模型,他把工作流内的某个功能,按照约定的接口暴露给外部使用者,为外部使用者提供数据,或要求外部使用者提供数据。
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。
- 同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用。
- 异步调用:一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。
- 回调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口。
回调和异步调用的关系非常紧密:使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。
2. Java接口回调
Java的接口支持提供了一种获得回调的机制,其技巧就是:定义一个简单接口,并在该接口中声明我们要调用的方法。
下面以一个示例来分析如何使用接口回调。
示例1
假如我们要发射火箭,由专门的人来发射(如专家、领导),如果我们要计算出火箭开始发射时间、发射结束时间及发送成功与否,那么可以使用回调机制来完成。
首先,建立一个接口:
package com.demo;
/**
* 该接口主要定义可回调的方法
*
* @author 小明
*
*/
public interface Delegate {
/**
* 开始发送时间
*
* @return 开始发送的时间
*/
long startTime();
/**
* 发送结束时间
*
* @return 结束发送的时间
*/
long endTime();
/**
* 发送发射成功与否的消息
*/
void sendFailedInfo();
}
建立火箭类:
package com.demo;
/**
* 火箭类,使用到回调接口中的方法
*
* @author 小明
*
*/
public class Rocket {
// 火箭将计时的操作委托给受托者处理
private Delegate delegate;
public Rocket(Delegate delegate) {
super();
this.delegate = delegate;
}
/**
* 获取开始发送时间
*
* @return 开始发送时间
*/
public long getRocketStartTime() {
return delegate.startTime();
}
/**
* 获取发送结束时间
*
* @return 结束发送时间
*/
public long getRocketEndTime() {
return delegate.endTime();
}
/**
* 判断是否发送成功
*
* @return 是否发送成功
*/
public boolean isOk() {
if (getRocketEndTime() - getRocketStartTime() <= 1000)
return true;
delegate.sendFailedInfo();
return false;
}
}
建立专家类:
package com.demo;
/**
* 专家类,实现回调接口,作为受托者身份出现
*
* @author 小明
*
*/
public class ProfessionalWorker implements Delegate {
@Override
public long startTime() {
return System.currentTimeMillis();
}
@Override
public long endTime() {
return System.currentTimeMillis();
}
@Override
public void sendFailedInfo() {
System.out.println("嘀...嘀...嘀...发射超时...");
}
/**
* 发射火箭
*/
public void send() {
if (new Rocket(this).isOk()){
System.out.println("发射成功!!!");
}
}
}
测试让专家发射火箭:
package com.demo;
public class Test {
public static void main(String[] args) {
ProfessionalWorker worker = new ProfessionalWorker();
worker.send();
}
}
再比如我们让领导来发射火箭:
package com.demo;
/**
* 领导
*
* @author 小明
*
*/
public class Leader {
/**
* 发送火箭
*/
public void send() {
// 使用匿名内部类实现委托操作
boolean isOk = new Rocket(new Delegate() {
@Override
public long startTime() {
return System.currentTimeMillis();
}
@Override
public void sendFailedInfo() {
System.out.println("嘀...嘀...嘀...发射超时...");
}
@Override
public long endTime() {
return System.currentTimeMillis() + 2000;
}
}).isOk();
if (isOk) {
System.out.println("发射成功");
}
}
}
测试让领导发射火箭:
package com.demo;
public class Test {
public static void main(String[] args) {
new Leader().send();
}
}
示例2
下面再举一个接口回调的例子,假如我们设计一个按钮,当点击这个按钮的时候得到点击操作的通知,我们先建立一个类:
package com.demo2;
/**
* 事件类
*
* @author 小明
*
*/
public class Event {
private String commandText; // 命令文本
private long when; // 触发时间
public Event(String commandText) {
super();
this.commandText = commandText;
this.when = System.currentTimeMillis();
}
/**
* 获取命令文本
*
* @return 命令文本字符串
*/
public String getCommandText() {
return this.commandText;
}
/**
* 获取事件触发时间
*
* @return 触发时间
*/
public long getWhen() {
return when;
}
}
那么,我们可以定义这样一个接口,用于事件监听:
package com.demo2;
/**
* 事件监听器接口
*
* @author 小明
*
*/
public interface Listener {
/**
* 监听动作
*
* @param event
* 事件
*/
void action(Event event);
}
再定义一个按钮类:
package com.demo2;
/**
* 按钮类
*
* @author 小明
*
*/
public class Button {
private String text; // 按钮文本
private Listener listener; // 监听器对象
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
/**
* 设置事件监听器
*
* @param listener
* 监听器对象
*/
public void setEventListener(Listener listener) {
this.listener = listener;
}
/**
* 点击按钮
*/
public void press() {
if (listener != null) { // 事件监听器不为空
Event event = new Event(this.getText());
listener.action(event);
}
}
}
如果我们要模拟按钮的点击事件,则先实现监听器的接口:
package com.demo2;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 实现事件监听器接口的类
*
* @author 小明
*
*/
public class MyEventListener implements Listener {
@Override
public void action(Event event) {
System.out.println("命令文本:" + event.getCommandText());
System.out.println("在" + dateToString(event.getWhen()) + "时,点击了按钮");
}
private String dateToString(long time) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date(time));
}
}
模拟实现点击:
package com.demo2;
/**
* 测试类
*
* @author 小明
*
*/
public class Test {
public static void main(String[] args) {
/* 创建按钮 */
Button btn = new Button();
btn.setText("登录");
/* 为按钮设置事件监听器 */
btn.setEventListener(new MyEventListener());
/* 模拟点击按钮 */
btn.press();
}
}
上述示例也是使用接口回调的方式实现的,是在Java Swing中典型的事件驱动机制处理方式。事件驱动机制采用委托方式实现,委托又通过接口回调来完成具体功能。