曾经在在面试时无数次被问到比较接口和抽象类的问题,过了这么多年,我一直没有找到非常权威的标准答案,今天来谈谈我对这个问题的看法。
1)一孔之见
可能是太多学习面向对象思想的人经常容易陷入使用接口还是抽象的纠结,于是就成了大家用来考验某个候选人对OOP理解深度的试金石。这个问题在我看来并不像听上去的那么高大上,江湖上被坑的人多了,就把本来不是问题的问题搞成了行业里普遍模糊的问题,因为没有绝对精确的答案,于是就让更多的后来人陷入糊涂之中。
其实回到这两个概念的初衷,完全可以顾名思义理解它们适用的场合,正规的书籍上也不会特意的把这两个拿在一块煞有介事的比较一番。从名字上看,所谓接口就是一个内部体系与外部体系约定的交互行为,更多强调的是如何约束外部的行为,而抽象类是对一个体系内部的共同行为进行统一抽象的描述,更多强调的是如何界定内部的行为。它们字面意思就给我们指示了各自的使用场合。(我这里说的体系不是都指一个完整的应用系统,可以是一个应用系统中的某个自成一体的抽象概念)。所以从这方面来看,你就根本不会在何时使用接口何时使用抽象类纠结了,当你设计的目标是一个和外部不同的体系交互时,那就用接口来描述抽象的行为特征;而当你设计的目标是一个自成一体的内部各个不同变化时自然就使用抽象类了。这如果不是接口和抽象类的全部使用场合,也会是它们的主要使用场景了。
2)模凌两可的设计思想差别
一些人试图从设计思想上解释这两个的区别,一种还算能容易理解的说法这样的:
假设接口 B implement A ,说明接口B和A之间是一种“B like A”的关系,
如果子类 B extend A,说明类B和抽象类A之间是一种“B is A”的关系。
可是如果我们较真的话,还真不好说like A和is A到底又有哪些区别,当在实际设计系统时,我们设计的目标好像在这两种关系中任何一个都可以。所以我说它是模凌两可地,在这里不能较真去一定要找一个最准的结果。
要我说,当在模凌两可时选择其中任何一个可以说也不一定就是什么错误的结果。
能说到的程度也就这样了,剩下的就靠读者去体会了。
3)完全能确定的语法上区别
比较点 | 抽象类 | 接口 |
可否有实现代码 | 可以写实现代码,而且最好把一些子类共同的或默认的行为逻辑写在抽象类中,把变化的部分留给子类实现 |
只能定义方法签名,不能写实现代码
|
可否多继承(实现) | 只能单一继承,java语法只支持继承一个类 | 一个类可以实现多个接口定义的方法。 |
是否必须实现定义的方法 | 抽象类的子类可以不实现全部方法,这时意味着这个子类也是抽象类。 | 必须实现所有接口定义了的方法 |
可否实例化 | 不可 | 不可 |
关键字 | Abstract,extend | Interface,implement |
如果再碰到这个问题,只能回答区别语法了,其它方面不建议放在一块比较。也希望后来者不要花过多精力试图去找到完美的答案。