接口与继承解决不同问题:继承表达“是什么”的is-a关系,强调状态与行为复用;接口表达“能做什么”的can-do契约,支持多实现与解耦;多态通过编译时类型与运行时类型的分离,协同二者实现灵活调度。

接口与继承不是同类概念,它们解决的是不同层面的问题:继承回答“它是什么”,接口回答“它能做什么”。多态则是让这两者协同工作的关键机制——不是替代关系,而是组合关系。
本质区别:关系模型与能力契约
继承是类型层级的纵向延伸,体现is-a语义。比如Dog extends Animal,意味着狗本质上是一种动物,天然拥有吃、睡等共性行为和状态。子类直接获得父类非私有字段和方法实现,耦合度高,复用性强。
接口是行为契约的横向声明,体现can-do或has-a语义。比如class Bird implements Flyable, Singable,不关心鸟是不是动物,只承诺它具备飞行和鸣叫的能力。接口不提供状态,也不强制实现方式,只约束必须提供的公开行为。
Java语言层面对此做了硬性隔离:
立即学习“Java免费学习笔记(深入)”;
- 一个类只能
extends一个父类(单继承),但可implements任意多个接口 - 接口中的字段自动为
public static final,方法默认public abstract(Java 8+支持default和static,但仍是契约补充) - 父类可含构造器、实例变量、具体方法;接口不能有构造器,也不能有普通实例变量
何时该用继承,而非接口
当存在明确的、稳定的“种类归属”且需共享**状态与行为实现**时,优先考虑继承。
典型场景包括:
- 基础组件复用:如
ArrayList继承AbstractList,复用迭代器、size()、isEmpty()等通用逻辑 - 领域模型建模:如
AdminUser extends User,管理员用户天然继承用户的身份、登录态、权限基类字段 - 模板方法模式:父类定义算法骨架(如
process()),子类只重写钩子方法(before()/after())
注意:滥用继承会导致类爆炸和脆弱的继承链。若只是为了复用某几个方法,优先提取工具类或组合,而非拉长继承树。
何时该用接口,而非继承
当需要解耦调用方与实现方,或同一类需具备多种独立能力时,接口是唯一合理选择。
常见适用情形:
- 跨类型统一操作:如
Comparable让String、Integer、自定义Person都能被Collections.sort()处理 - 角色化能力叠加:如
Car implements Movable, Insurable, Taxable,每个接口代表一个业务维度,互不干扰 - 框架扩展点:Spring 的
InitializingBean、MyBatis 的Interceptor,都靠接口约定生命周期回调入口 - 避免“伪继承”:比如
TimerTask本不该是Runnable的子类,但用implements Runnable就能自然融入线程池体系
多态如何串联继承与接口
多态本身不依赖继承或接口,但它在实践中几乎总是依托二者之一来落地。核心在于编译时类型与运行时类型的分离。
两种主流多态形式:
-
基于继承的多态:父类引用指向子类对象
Animal a = new Dog(); a.eat();→ 调用Dog.eat(),运行时决定 -
基于接口的多态:接口引用指向任意实现类对象
Payment p = new WechatPay(); p.pay();或p = new Alipay(); p.pay();
真正强大的设计,是混合使用:
- 用继承构建领域主干(如
Order→RefundOrder→ExchangeOrder) - 用接口注入横切能力(如所有订单都
implements Loggable, Auditable) - 再通过多态统一调度:
void handle(Order order)方法内部,既可调用order.cancel()(继承链方法),也可调用((Loggable)order).log()(接口能力)
这种分层+契约+动态绑定的结构,才是应对复杂业务演化的可持续路径。


















