写在前面
最近对于接口的应用也算比较多吧,所以来总结一波我对于接口的使用和感悟。
比抽象类更加抽象
说到接口,就不得不提抽象类了。这二者有很多相似和很多不同的地方,但是要我说这二者最大的不同,就是标题了:接口比抽象类更加抽象。这个结论通常情况下都是成立的,除非你的抽象类什么都没……但是一般情况下你也不会弄这么个没意义的抽象类。一般来说在设计抽象类时都会考虑到这个抽象类应当实现什么功能和恰当的设计其抽象方法,前者是此抽象类的意义,后者通常是交给使用者去实现其需要的具体的功能。接口则不然,他只需要考虑一个类实现了这个接口的类“是怎样的”,需要我们做的就是恰当的设计一个接口该拥有哪些方法(这一点在java8之后略有不同,因为接口方法可以有默认的实现了),并不需要在接口里去具体的实现。从这一点看来接口比抽象类更加的抽象。
另外抽象类还有一个使用的技巧——防止类被实例化,这一点在《Thinking in java》中亦有提及,因为接口和抽象类都不能被实例化。这点是个概念问题,我们代码中看起来是实例化接口或者抽象类,实际上都是用的一个匿名类的对象。
解耦利器
使用接口非常重要的一点就是:解耦。
在平时写代码的时候我们都会不自觉的敲出如下代码:
List<String> list = new ArrayList<>();
前面的List便是一个接口,在实现相应的方法的时候,我们也会将参数尽可能的写成其接口。这使得我们的代码不依赖于具体的数据类型,只要是List的实现类即可。在这个角度看来,其的确实现了代码的解耦。
在Java中,回调大多是用接口来实现的,结合一个android中的实例来做一些更加深入的思考:
view.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View view){
//........
}
});
这是android种一个典型的回调的例子。OnClickListener是一个接口,在view的内部会持有一个OnClickListener的引用,在你设置了这个OnClickListener之后,如果有点击事件发生,会回调这个接口方法。这是回调简单的流程概述,那么再深入思考一下,这种形式不能用调用类的方法来实现么?
答案当然是可以,首先你要理解一点:接口是不会干活的(恩,还是先将java8排除在外好了)。当我们在设置OnClickListener的时候,我们是通过匿名内部类来实现了一个接口。回调可以分成两个角度来叙述,首先从系统角度来说,回调是一些特殊事件发生时(比如点击事件),我会通知你我这事发生了。从我们开发者角度来说,就是我希望能在特殊事件发生时(点击事件),我能有一个方法来做一些事(响应客户端的点击事件)。那么在实现的时候我们就可以在对应的位置调用一个方法(通常是空方法),然后让开发者实现这个方法不就万事大吉了么?事实上也是如此,就算不用接口我们用一个带方法内容为空的类来试试。
public class OnClickListener {
public void onClick(String s){};
}
上面的代码可以清晰的看出这是个类,只不过我起的名字是那玩意。接下来看具体的实现,为了方便我都写了静态方法,就不用再写第三个类了:
public class A {
public static OnClickListener listener;
public static void setOnClickListener(OnClickListener listener){
A.listener = listener;
}
public static void click(){
//这个方法代表被点击了
System.out.println("被点击了");
listener.onClick("被点击了--->回调发生了");
}
public static void main(String[] args){
setOnClickListener(new OnClickListener(){
@Override
public void onClick(String s){
System.out.println(s);
}
});
click();
}
}
由上可见,类也能实现回调,只要用户重写相应的方法就行了。事实上接口不也是如此么?只不过我们将重写接口方法称作实现罢了。
以上其实我觉得并不能全算为接口的功劳,这些解耦的功劳** 多态 ** 能分一半。
多继承 & 协议
Java中没有多重继承,只是单继承,但是利用接口,我们可以变相的实现多继承。还是刚才android中经典的例子,我们有时为了点击事件的统一编写,可能会这么干:
setOnClickListener(this);
这个this就是我们的Activity或者其他的玩意,那么我们可以思考一下,这时的Activity既是Activity某个父类的子类,也是OnClickListener类。
回到刚开始所说的,接口的特性是啥?就是实现这个接口的类“看起来都是这样的”,有没有一种很熟悉的既视感?是了,我们常用的数据格式json、xml不都是“看起来都是这样的”么,只要是符合规范和双方协定的json或者xml,不管其具体内容是什么,我们都可以解析和使用。那么在设计和实现接口时也应当如此。比如Java集合框架的Collection接口就约定在实现boolean add(E e)方法时,如果拒绝添加一个元素应当抛出一个异常而不是返回false。当我们需要自己实现一个Collection接口的类时应当遵循这些约定,不然其他遵循这些约定而设计的方法可能会不适用于你的类。
以上是基于设计方面的一些小结,事实上我们并没有多少机会去设计接口……通常我们实现的接口都是用来被回调的,也就是实现某个功能而已。但是咋说呢,多了解点,让眼界开阔点也好。
最后觉得我好像该去看看设计模式了……