表示接口代表合同的答案是不可接受的。这就是我们给Junior的答案,因为它可能太复杂而无法清楚地了解抽象类的本质与接口的本质之间的区别,而无需太多的架构经验,也无需阅读大量经典书籍。任何带有
public方法的抽象类都充当协定和接口。
不提供任何实现的抽象类在99%的情况下表示对象的 Role 。
一个 接口 表示 角色 。
每个对象可能具有多个不同的角色,这些角色不应捆绑在一起,而应由相关的对象组成。
我在这个例子中解释这个 :
您的面试官可能会说:
我也有
Robot可以走路的东西
Human,也可以走路的东西。
因此,基于这种情况,他问你:我是否应该在知道实现没有共同点的情况下,在抽象基类或接口中提取步行功能?
您认为……“哦,我知道:在这种情况下,拥有一个具有抽象方法的抽象类,
walk()显然与声明该
walk()方法的接口相同。”
因此,您的答案肯定是:“这是开发人员的选择!”。
这确实不是一个始终有效的答案。
为什么?让我们看看下一个期望:
A
Human可以吃,但是显然
Robot不能甚至不需要。
如果您用抽象类实现了步行功能怎么办?您最终将得到:
public abstract class Biped { public void abstract walk();}public Robot extends Biped { public void walk() { //walk at 10km/h speed }}public Human extends Biped { public void walk() { //walk at 5km/h speed }}您如何才能插入该
eating功能?您之所以陷入困境是因为您无法在
Biped基类中实现它,因为它会破坏 Liskov替换原理 ,因为a
Robot不会吃!而且,
Human由于已知的Java规则,您不能期望扩展另一个基类。
当然,您可以添加仅专用于Human的特定Feedable接口:
public interface Feedable { void eat();}签名变为:
public Human extends Biped implements Feedable { 显然,让一个 角色通过一个类来实现而另一个 角色 通过接口来实现是没有任何意义和混乱的。
这就是为什么只要有选择,就真正优选从接口开始。
通过一个界面,我们可以轻松地对 角色 进行建模。
因此,最终的解决方案将是:
public interface Walkable { void abstract walk();}public interface Feedable { void eat();}public Robot implements Walkable { public void walk() { //walk at 10km/h speed }}public Human implements Walkable, Feedable { public void walk() { //walk at 5km/h speed } public void eat(){ //... } }它不会提醒您 接口隔离原则 吗?;)
综上所述,如果您指定 IS-A 关系,请使用抽象类。如果您意识到要建模一个 角色 (假设是 IS-CAPABLE-OF
关系),请使用interface。



