Java接口与回调

1.接口的基本概念

1.1、interface是对类的一组需求的描述,但不给出需求的具体实现。
1.2、接口不是类,不能使用new来实例化一个接口,即不能构造接口的对象。但是我们其实经常可以与此命题相矛盾的代码,实质上是匿名内部类。
1.3、尽管不能使用new实例化一个接口,但是可以声明接口的变量。接口变量必须引用一个实现了接口的对象 才能发挥用处。
1.4、接口不能包含实例域或静态方法,但是可以包含常量,且实现了该接口的类都会自动继承这些常量,并且可以直接引用这些常量。(这种应用实际上偏离了接口概念的初衷,尽量少用)
1.5、接口的好处:一个类可以实现多个接口,这弥补了“java中一个类只能继承一个父类”的局限性。
1.6、接口的常见应用:①解决多重继承问题; ②定义一个规范(协议); ③用于回调。

2.接口的简单应用

应用1:解决多重继承问题

Java语言本身是不支持类的多重继承,但一个类却可以实现多个接口。这样,我们可以将一些抽象方法定义在接口中,间接地达到多重继承的目的。例如:

public interface MyInterface1 {
    void fly();
}

public interface MyInterface2 {
    void walk();
}

public class Bird implements MyInterface1, MyInterface2 {
    private static final String TAG = "Bird";
    @Override
    public void fly() {
        Log.i(TAG, "I can fly");
    }

    @Override
    public void walk() {
        Log.i(TAG, "I can walk");
    }
}

应用2:定义一个规范(协议)

同一个接口可以有多个不同的实现类,但每一个实现类都必须重写接口中所有的抽象方法。即接口不考虑这些实现类各自采用什么方式实现这些功能,但它要求所有的实现类都必须有这些功能。

3.接口与回调

回调的基本实现方法,分为三部分。
第一部分:定义接口A。
第二部分:定义了B类,B实现了A接口(必定也实现了接口规定的方法),将B的实例 作为参数,传入到C类实例中(通过C的构造函数传入参数 或者 通过调用C类实例的方法传入参数)。
第三部分:定义了C类,C类可以接收第二部分传来的参数(构造函数的参数 或 某个方法的参数 的类型设定为A),接收到这个参数后,就可以使用这个参数来调用B类中的方法(比如在适当的地方执行mOnClickListener.onClick(this))。
例1:这篇文章的“用于回调”部分 http://www.jianshu.com/p/cccb430a8ba0
例2:http://blog.csdn.net/xiaanming/article/details/8703708/
例3:Android中的OnClickListener的使用(例2文章中也提到了),这里稍微分析下。OnClickListener即对应A,而B是implement了A的类(或者用匿名类),View(或其子类)对应C。View中通过setOnClickListener这个函数接收参数B,并保存在mOnClickListener中,在适当的地方执行mOnClickListener.onClick(this)便调用了B的onClick方法,并且将自己(this)传递给B,从而实现了回调。回调时带回的参数(这里是个view),可以告诉B 这是哪个view触发了onClick(当然,如果每个button都用匿名内部类方式设置监听,则view和监听是一对一的关系)。

第一部分和第三部分写法变化不大,第二部分有多种写法,以OnClickListener为例。
写法1:采用内部类。MainActivity中创建了myListener这个类。

class myListener implements OnClickListener
{
    @Override
    public void onClick(View v) {
   //具体点击操作的逻辑
   }
}
OnClickListener listener=new myListener();
button.setOnClickListener(listener);

写法2:匿名内部类。

OnClickListener listener=new OnClickListener()
{
    @Override
    public void onClick(View v) {
   //具体点击操作的逻辑
   }
}
button.setOnClickListener(listener);

注意 listener应该理解为一个实现了OnClickListener接口的不知名的类的实例。

写法3:匿名内部类精简版

button.setOnClickListener(new OnClickListener()
{
    @Override
    public void onClick(View v) {
   //具体点击操作的逻辑
   }
});

写法4:lambda表达式版

button.setOnClickListener((view)->{//具体点击操作的逻辑});

写法5:MainActivity实现OnClickListener接口,然后将自己作为参数传入。

button.setOnClickListener(this)

当然,传入的this是MainActivity的当前实例。(this关键字可以代表自身类的对象)(传入的参数必然是个实例,不可能传入“类”)

4.一些思考

4.1、android程序中,甚至只需要在布局中设置android:onClick="onClick"而不需要像4.1那样的代码。查看View源码可知,若设置onClick这个属性,便会重新setOnClickListener。顺便提一句,这篇文章总结了“按钮设置监听”的一些方式 http://www.jb51.net/article/108043.htm 当然任何view都可以设置监听,其中Button的clickable默认为true。
此外,从View源码中可以看出, setOnClickListener中会再次将clickable设置为true,因此若想设定clickable为false,必须在setOnClickListener调用之后设定。
4.2、B和C的对应关系,以上许多例子为1对1关系。
一对多:在MainActivity实现OnClickListener,并且有多个button时,可以通过回调带回来的参数view识别。
多对一:比如一个button先后设置了几个不同的OnClickListener,那么应该以最后一个为准。但是这里的打电话的例子会不会出问题:
http://blog.csdn.net/xiaanming/article/details/8703708/,或者有没有其他情况会出bug。
4.3、能否用抽象类或者超类之类的代替接口,实现回调。

5.参考资料

《Java核心技术》(第10版)
http://www.jianshu.com/p/cccb430a8ba0
http://blog.csdn.net/xiaanming/article/details/8703708/
http://www.jb51.net/article/108043.htm

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章