困惑已久的问题
在 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);
在最后一种写法中,我们没有采用匿名类,而是创建了一个实现了接口的类。
Last modified on 2020-06-29