1 接口回调
1.1 接口回调概念
什么是接口回调
接口回调
是指:可以把使用某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法。实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法,这一过程称为对象功能的接口回调。
interface People{
void peopleList();
}
class Student implements People{
public void peopleList(){
System.out.println("I’m a student.");
}
}
class Teacher implements People{
public void peopleList(){
System.out.println("I’m a teacher.");
}
}
public class Example{
public static void main(String args[]){
People a; //声明接口变量
a=new Student(); //实例化,接口变量中存放对象的引用
a.peopleList(); //接口回调
a=new Teacher(); //实例化,接口变量中存放对象的引用
a.peopleList(); //接口回调
}
}
结果:
I’m a student.
I’m a teacher.
再来看看向上转型(upcasting)的概念。
1.2 向上转型
Shape s=new Circle();
,这里,创建了一个Circle
对象,并把得到的引用立即赋值给Shape
。通过继承,Circle
就是一种Shape
。
假设你调用基类方法(它已在导出类中被覆盖):
s.draw();
由于后期绑定(多态
),将会正确调用Circle.draw()
方法。
1.3向上转型与接口回调的区别
看似向上转型和接口回调是一回事。看下面两句话,均出自Thinking in Java
使用接口的核心原因:为了能够向上转型为多个基类型。即利用接口的多实现,可向上转型为多个接口基类型(具体见《抽象与接口》章节6)。
从实现了某接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样的。(此句摘自Thinking in Java 3rd 接口与内部类一章)
所以,这两个概念是从两个方面来解释一个行为。接口回调的概念,强调使用接口来实现回调对象方法使用权的功能。而向上转型则牵涉到多态和运行期绑定的范畴。
1.4 用Java接口实现回调函数的等价功能
熟悉 MS-Windows
和 X Window System
事件驱动编程模型的开发人员,习惯于传递在某种事件发生时调用(即回调
)的函数指针。Java
的面向对象模型目前并不支持方法指针,Java
的接口支持提供了一种获得回调的等价功能的机制。其技巧就是:定义一个简单接口,并在该接口中声明我们要调用的方法。
假定我们希望在某个事件发生时得到通知。我们可以定义一个接口:
InterestingEvent.java
package org.zj.sample;
public interface InterestingEvent {
public void interestingEvent ();
}
这使得我们可以控制实现该接口的类的任何对象。因此,我们不必关心任何外部类型信息。
发出事件信号的类必须等待实现了 InterestingEvent 接口的对象,并在适当时候调用 interestingEvent() 方法。
EventNotifier.java
package org.zj.sample;
public class EventNotifier {
private InterestingEvent ie;
private boolean somethingHappened;
public EventNotifier(InterestingEvent event) {
ie = event; // 保存事件对象以备后用。
somethingHappened = false; // 还没有要报告的事件。
}
public void doWork() {
if (somethingHappened) { // 检查设置的谓词。
ie.interestingEvent();// 通过调用接口的这个方法发出事件信号。
}
}
public void setHappened(){//设置谓词。
somethingHappened=true;
}
}
在上例中,使用 somethingHappened
谓词来跟踪是否应触发事件。希望接收事件通知的代码必须实现 InterestingEvent
接口,并将自身引用传递给事件通知程序。
CallMe.java
package org.zj.sample;
public class CallMe implements InterestingEvent {
@SuppressWarnings("unused")
private EventNotifier en;
public CallMe() {
// 注意 EventNotifier (InterestingEvent event),应该传递一个接口类型。
// 而下面将this,即实现了InterestingEvent接口的CallMe实例传递给
//EventNotifier。也就是所谓的接口回调了。
en = new EventNotifier(this); // 创建事件通知程序,并将自身引用传递给它。
}
// 为事件定义实际的处理程序。
public void interestingEvent() {
System.out.println("Call me Hello.");
}
}
下面写个测试类。
Test.java
package org.zj.sample;
public class Test {
public static void main(String[] args) {
EventNotifier en=new EventNotifier(new CallMe());
en.setHappened();
en.doWork();
}
}
结果:
Call me Hello.
1.5 通过例子深入理解
所谓回调:就是A类
中调用B类
中的某个方法C
,然后B类
中反过来调用A类
中的方法D
,D
这个方法就叫回调方法,这样子说你是不是有点晕晕的,看了人家说比较经典的回调方式:
- 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
/**
* 这是一个回调接口
* @author xiaanming
*
*/
public interface CallBack {
/**
* @param result 是答案
*/
public void solve(String result);
}
/**
* @author xiaanming
* 实现了一个回调接口CallBack,相当于----->背景一
*/
public class Wang implements CallBack {
/**
* 相当于----->背景二
*/
private Li li;
/**
* @param li
*/
public Wang(Li li){
this.li = li;
}
/**
* @param question
*/
public void askQuestion(final String question){
//这里用一个线程就是异步,
new Thread(new Runnable() {
@Override
public void run() {
/**
* 这就相当于A类调用B的方法C
*/
li.executeMessage(Wang.this, question);
}
}).start();
play();
}
public void play(){
System.out.println("我要逛街去了");
}
/**
*
*/
@Override
public void solve(String result) {
System.out.println("小李告诉小王的答案是--->" + result);
}
}
/**
* @author
*
*/
public class Li {
/**
* 相当于B类有参数为CallBack callBack的f()---->背景三
* @param callBack
* @param question 小王问的问题
*/
public void executeMessage(CallBack callBack, String question){
System.out.println("小王问的问题--->" + question);
//模拟事情需要很长时间
for(int i=0; i<10000;i++){
}
/**
* 小李办完自己的事情之后想到了答案是2
*/
String result = "答案是2";
/**
* 于是就打电话告诉小王,调用小王中的方法
* 这就相当于B类反过来调用A的方法D
*/
callBack.solve(result);
}
}
/**
* 测试类
* @author xiaanming
*
*/
public class Test {
public static void main(String[]args){
Li li = new Li();
Wang wang = new Wang(li);
wang.askQuestion("1 + 1 = ?");
}
}
通过上面的那个例子你是不是差不多明白了回调机制呢,
再看下面的例子
//这个是View的一个回调接口
/**
* Interface definition for a callback to be invoked when a view is clicked.
*/
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
package com.example.demoactivity;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
/**
* 这个就相当于Class A
* @author xiaanming
* 实现了 OnClickListener接口---->背景一
*/
public class MainActivity extends Activity implements OnClickListener{
/**
* Class A 包含Class B的引用----->背景二
*/
private Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button1);
/**
* Class A 调用View的方法,而Button extends View----->A类调用B类的某个方法 C
*/
button.setOnClickListener(this);
}
/**
* 用户点击Button时调用的回调函数,你可以做你要做的事
* 这里我做的是用Toast提示OnClick
*/
@Override
public void onClick(View v) {
Toast.makeText(getApplication(), "OnClick", Toast.LENGTH_LONG).show();
}
}
下面是View
类的setOnClickListener
方法,就相当于B类咯,只把关键代码贴出来
/**
* 这个View就相当于B类
* @author xiaanming
*
*/
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
/**
* Listener used to dispatch click events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected OnClickListener mOnClickListener;
/**
* setOnClickListener()的参数是OnClickListener接口------>背景三
* Register a callback to be invoked when this view is clicked. If this view is not
* clickable, it becomes clickable.
*
* @param l The callback that will run
*
* @see #setClickable(boolean)
*/
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}
/**
* Call this view's OnClickListener, if it is defined.
*
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//这个不就是相当于B类调用A类的某个方法D,这个D就是所谓的回调方法咯
mOnClickListener.onClick(this);
return true;
}
return false;
}
}
网友评论