一.什么是接口回调

接口回调是指:可以把使用某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法。实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法,这一过程称为对象功能的接口回调。看下面示例。通俗点来讲就是:我这个类实现了一个接口里的方法doSomething,然后注册到你这里,然后我就去做别的事情去了,你在某个触发的时机回头来调用我doSomething的方法。好比,我给你打电话问某个难题,你电话里想不出来,于是说等想到了再回我电话。于是接口回调机制比喻模型如下:


java 接口回调写法 java的接口回调_Java

1.interface People{  
2.   void peopleList();  
3.}  
4.class Student implements People{  
5.   public void peopleList(){  
6.    System.out.println("I’m a student.");  
7.}  
8.}  
9.class Teacher implements People{  
10.  public void peopleList(){  
11.    System.out.println("I’m a teacher.");  
12.}  
13.}  
14.public class Example{  
15.  public static void main(String args[]){  
16.    People a;             //声明接口变量  
17.a=new Student();      //实例化,接口变量中存放对象的引用  
18.a.peopleList();        //接口回调  
19.a=new Teacher();     //实例化,接口变量中存放对象的引用  
20.a.peopleList();       //接口回调  
21.}  
}

结果:

I’m a student.

I’m a teacher.

二.什么是向上转型?


java 接口回调写法 java的接口回调_接口回调_02

Shape s=new Circle();

这里,创建了一个Circle对象,并把得到的引用立即赋值给Shape。通过继承,Circle就是一种Shape。

假设你调用基类方法(它已在导出类中被覆盖):

s.draw();

由于后期绑定(多态),将会正确调用Circle.draw()方法。



三.向上转型与接口回调的区别

看似向上转型和接口回调是一回事。看下面两句话,均出自Thinking in Java。

使用接口的核心原因:为了能够向上转型为多个基类型。即利用接口的多实现,可向上转型为多个接口基类型(具体见《抽象与接口》章节6)。

从实现了某接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样的。(此句摘自Thinking in Java 3rd 接口与内部类一章)

所以,我认为,这两个概念是从两个方面来解释一个行为。接口回调的概念,强调使用接口来实现回调对象方法使用权的功能(下一章节详细分析)。而向上转型则牵涉到多态和运行期绑定的范畴。


四.Java 接口实现回调函数的等价功能

熟悉 MS-Windows 和 X Window System 事件驱动编程模型的开发人员,习惯于传递在某种事件发生时调用(即“回调”)的函数指针。Java 的面向对象模型目前并不支持方法指针,Java 的接口支持提供了一种获得回调的等价功能的机制。其技巧就是:定义一个简单接口,并在该接口中声明我们要调用的方法。

假定我们希望在某个事件发生时得到通知。我们可以定义一个接口:

InterestingEvent.java
1. public interface InterestingEvent {  
2.     public void interestingEvent ();  
}

这使得我们可以控制实现该接口的类的任何对象。因此,我们不必关心任何外部类型信息。

发出事件信号的类必须等待实现了 InterestingEvent 接口的对象,并在适当时候调用 interestingEvent() 方法。

EventNotifier.java

1. public class EventNotifier {  
2.     private InterestingEvent ie;  
3.     private boolean somethingHappened;  
4.    
5.     public EventNotifier(InterestingEvent event) {  
6.          ie = event; // 保存事件对象以备后用。
7.          somethingHappened = false; // 还没有要报告的事件。
8.     }   
9.     public void doWork() {  
10.          if (somethingHappened) { // 检查设置的谓词。
11.            ie.interestingEvent();// 通过调用接口的这个方法发出事件信号。
12.          }  
13.     }  
14.       
15.     public void setHappened(){//设置谓词。
16.          somethingHappened=true;  
17.     }  
}

在上例中,使用 somethingHappened 谓词来跟踪是否应触发事件。希望接收事件通知的代码必须实现 InterestingEvent 接口,并将自身引用传递给事件通知程序。

CallMe.java

1. public class CallMe implements InterestingEvent {  
2.     @SuppressWarnings("unused")  
3.     private EventNotifier en;  
4.    
5.     public CallMe() {  
6.          // 注意 EventNotifier (InterestingEvent event),应该传递一个接口类型。
7.          // 而下面将this,即实现了InterestingEvent接口的CallMe实例传递给
8. //EventNotifier。也就是所谓的接口回调了。
9.          en = new EventNotifier(this); // 创建事件通知程序,并将自身引用传递给它。
10.     }  
11.    
12.     // 为事件定义实际的处理程序。
13.     public void interestingEvent() {  
14.               System.out.println("Call me Hello.");  
15.     }  
}


测试类

Test.java


1. public class Test {  
2.     public static void main(String[] args) {  
3.          EventNotifier en=new EventNotifier(new CallMe());  
4.          en.setHappened();  
5.          en.doWork();  
6.     }  
}