1.接口回调是什么?
接口回调是指:可以把使用实现了某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法。实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法,这一过程称为对象功能的接口回调。看下面示例。
I’m a student.
I’m a teacher.
再来看看向上转型(upcasting)的概念。
2.什么是向上转型[1]?
这里,创建了一个Circle对象,并把得到的引用立即赋值给Shape。通过继承,Circle就是一种Shape。
假设你调用基类方法(它已在导出类中被覆盖):
由于后期绑定(多态),将会正确调用Circle.draw()方法。
3.Java中的跨类引用
在java里面,关于跨类引用,有两条规则应该记住:
1)如果a是类A的一个引用,那么,a可以指向类A的一个实例。或者说指向类A的一个子类,这是向上转型的情形。
2)如果a是接口A的一个引用,那么,a必须指向实现了接口A的一个类的实例。这是接口回调的情形。
在java里面,向上转型是自动进行的,但是向下转型却不是,需要我们自己定义强制进行.
接口回调是指:可以把使用实现了某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法。实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法,这一过程称为对象功能的接口回调。看下面示例。
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)的概念。
2.什么是向上转型[1]?
Shape s=new Circle(); |
这里,创建了一个Circle对象,并把得到的引用立即赋值给Shape。通过继承,Circle就是一种Shape。
假设你调用基类方法(它已在导出类中被覆盖):
s.draw(); |
由于后期绑定(多态),将会正确调用Circle.draw()方法。
3.Java中的跨类引用
在java里面,关于跨类引用,有两条规则应该记住:
1)如果a是类A的一个引用,那么,a可以指向类A的一个实例。或者说指向类A的一个子类,这是向上转型的情形。
2)如果a是接口A的一个引用,那么,a必须指向实现了接口A的一个类的实例。这是接口回调的情形。
在java里面,向上转型是自动进行的,但是向下转型却不是,需要我们自己定义强制进行.
class B extends A{} public class A { public static void main(String[] argts){ A a1=new A(); A a2=new B();//Upcasting B b1=new B(); B b2=(B) new A();//DownCasting } } |
4.向上转型与接口回调的区别
看似向上转型和接口回调是一回事。看下面两句话,均出自Thinking in Java。
使用接口的核心原因:为了能够向上转型为多个基类型[1]。即利用接口的多实现,可向上转型为多个接口基类型(具体见《抽象与接口》章节6)。
从实现了某接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样的。这些对象都可以调用基类型提供的方法,对于接口来说就是回调接口中的方法,对于父类来说就是调用父类的方法。当然在向上转型的情况下,还牵涉到子类重写(Override)父类方法的情形。(此句摘自Thinking in Java 3rd 接口与内部类一章)
所以,我认为,这两个概念是从两个方面来解释一个行为。接口回调的概念,强调使用接口来实现回调对象方法使用权的功能(下一章节详细分析)。而向上转型则牵涉到多态和运行期绑定的范畴。
5.用 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.