1.背景
类是对象的抽象,是一个特征集,我们会定义一些成员变量和构造函数来体现不同实例之间的差异。比如孩子中会有名字等
当然在类中也会有一些成员方法,如孩子们可以玩游戏等。
这些都是我们可以根据需求与实际预知并抽象出来的,但是如果有这样一种行为,对象自己无法控制什么时候会发生,如孩子们被告知回家吃饭,然后每个孩子心里所想,做什么我们都无法预知。而这些无法用简单的成员变量去描述。
一千个孩子有一千个孩子的想法,所以除非我们事先知道每个孩子的想法,然后预先创建一千个想法的孩子类,可是刚好只有一个孩子被叫着吃饭了,其余的就浪费了。
所以必须有有这样一种机制来处理这种情况:同一类事物遇到相同一个事件,所表现的行为不同。
在孩子类中显然不能处理这件事情,因为孩子的数量我们无法确定,如果增加一个孩子就要去修改孩子类,显然是不合理的。
所以需要把孩子“被叫回家吃饭”这个事件拉出来单独处理,交给别人去做,于是就有了监听器。
2.原理与实例
监听器,负责监听事件的发生,在Java中,就是一个接口,里面包含事件方法
interface EventListener {
public void event();
}
对于需要监听的对象,也就是“事件源”,我们需要添加一个监听器,当事件源事件被出发时,我们将它交给监听器
class EventSource{
EventListener listener;
public void event(){
listener.event();
}
}
到现在,我们已经将事件交给了监听器去处理,但是监听器需要去实例化才能做事情,也就是注册监听器
class EventSource{
EventListener listener;
public void event(){
listener.event();
}
public void addEventListener(EventListener listener){
this.listener = listener;
}
}
这样,我们只需要为每个事件源注册一个监听器,让注册的监听器去处理事件发生后需要做的事情
class Test{
public static void main(String[] args){
EventSource es = new EventSource();
es.addEventListener(new EventListener() {
@Override
public void event() {
// TODO Auto-generated method stub
}
});
}
}
我们来看一下流程,首先我们需要创建EventSource,并添加一个listener,当EventSource的event被触发,会执行listener.event();联系前面讲到的多态,便实现了事件监听的过程。
联系下回调与事件监听,回调是一种思想,它可以实现代码的执行时间不在依赖于自身的模块,而是由其他模块控制,无论面向对象还是面向过程。
而事件监听是利用回调机制结合接口的特点实现的一种模式,也就是观察者模式。
我们实现一下孩子被妈妈叫去吃饭的例子:
interface Listener {
public void istoldBack();
}
class Children{
String name;
Listener backhomeListener;
public Children(String name){
this.name = name;
}
public void playing(){
System.out.println(name + "is playing()");
}
public void backHome(){
backhomeListener.istoldBack();
}
public void addBackhomeListener(Listener backhomeListener){
this.backhomeListener = backhomeListener;
}
public String getName(){
return this.name;
}
}
class Mom{
public void backHome(Children child){
System.out.println("妈妈:"+child.getName()+"回家吃饭了");
child.backHome();
}
}
public class ListenerTest{
public static void main(String[] args){
Mom mom = new Mom();
Children xiaoming = new Children("xiaoming");
Children xiaohong = new Children("xiaohong");
xiaoming.playing();
xiaohong.playing();
xiaoming.addBackhomeListener(new Listener() {
@Override
public void istoldBack() {
// TODO Auto-generated method stub
System.out.println("xiaoming:我要回家吃饭");
}
});
xiaohong.addBackhomeListener(new Listener() {
@Override
public void istoldBack() {
// TODO Auto-generated method stub
System.out
.println("xiaohong:我不回去吃了");
}
});
mom.backHome(xiaoming);
mom.backHome(xiaohong);
}
}
运行结果:
xiaomingis playing()
xiaohongis playing()
妈妈:xiaoming回家吃饭了
xiaoming:我要回家吃饭
妈妈:xiaohong回家吃饭了
xiaohong:我不回去吃了
3.疑问
匿名类,看以下代码:
xiaoming.addBackhomeListener(new Listener() {
@Override
public void istoldBack() {
// TODO Auto-generated method stub
System.out.println("xiaoming:我要回家吃饭");
}
});
Listener明明是是个接口,如何实例化的?
匿名类有两种实现方式,一种是继承一个类,重写其方法,另一种是实现一个接口
此处是实现了一个接口,newListener()实质上是实例化了一个继承Listener这个接口的子类,而并非实例化了接口。
使用匿名类只是为了一些特殊的实现,比如此处就是为了实现Listener中的istoldBack()方法,当让你也可以创建一个新类继承这个接口,然后再去实现,但是使用匿名类是代码更简洁,模块也清晰。