困惑已久的问题 #
在 Android 开发过程中,下面这种模板代码是很常见的:
btn.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
//Do something
}
});一个很自然的想法是,每次需要设置点击监听就粘贴以上模板代码再做修改就好了。然而,有一个不容忽视的问题:调用 setOnClickListener 方法时,究竟传入了一个什么类型的参数?
接口与实现 #
接口(interface)是 OOP 中的一个概念。它描述了一类行为,但由于过于抽象的缘故,不能成为一个类(Class)。如果一个类实现(implements)了某接口,那么这个类就具有接口中定义的(具体的)方法。上述说法较难理解,下面举一个著名的“报警器与门”的例子。
假设有一个接口 Alarm,其中有方法 alarm();另有一个类 Door,并且 Door 类实现了接口 Alarm:
public interface Alarm {
public void alarm();
}
public Class Door implements Alarm {
// Some properties and methods
// belonging to Door
// ...
@Override
public void alarm() {
// Do something
}
}如果另一个类 Car 也具有报警器的功能,那么它也可以实现接口 Alarm。
public Class Car implements Alarm {
// Some properties and methods
// belonging to Car
// ...
@Override
public void alarm() {
//Do something else
}
}两个类都分别提供了 Alarm.alarm 的具体实现,且它们不一定相同。
显然地,interface 本身不应被实例化,因为它就是一个“画中之饼”;类才可以被实例化,而在本例中,就是实现了接口 Alarm 的 Door 类和 Car 类可以被实例化。
匿名类和匿名对象 #
让我们回到最初的问题:setOnClickListener 方法究竟传入了什么参数?
首先,代码可以做初步的改写,以变得更加清晰:
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
//Do something
}
}
btn.setOnClickListener(listener);首先,以上代码是没有问题的;然而,我们震惊地发现,View.OnClickListener 是一个接口(interface),它似乎通过 new 关键字被实例化了!事实果真如此?
并不是。接口并不能被实例化。上述操作相当于先创建了某个匿名类,这个匿名类实现了接口 View.OnClickListener,new 作用在这个匿名类上,实例化的实质上是该匿名类。到这里,我终于弄懂了被传入方法 setOnClickListener 中的参数 listener 的类型:它正是这个匿名类的一个实例化的对象。
有一个更加清晰的写法可以帮助我们理解以上说法:
public Class MyListener implements View.OnClickListener {
@Override
public void onClick(View v) {
//Do something
}
}
MyListener listener = new MyListener();
btn.setOnClickListener(listener);在最后一种写法中,我们没有采用匿名类,而是创建了一个实现了接口的类。