接口比抽象类更适合作为契约,因其强制零状态、零实现,聚焦行为本身;支持正交组合与能力命名,避免隐式绑定和耦合。

因为接口只定义“能做什么”,不掺杂“是什么”或“怎么做”的暗示;抽象类容易悄悄带入状态、构造逻辑和默认实现,让契约变味。
接口强制零状态、零实现
接口里不能定义普通字段、不能写构造方法、不能有 protected 成员。所有字段默认是 public static final,所有方法默认是 public abstract(Java 8+ 的 default/static 方法只是辅助,不改变契约本质)。这种限制让调用方完全聚焦在行为本身:
- PaymentProcessor 接口只声明 process() 和 refund(),使用者无需知道背后是支付宝还是模拟对象
- Logger 接口只承诺 log(String msg),不暴露日志级别字段或格式化逻辑
- 一旦加入字段或非抽象方法,就不再是契约,而是具体实现的影子
抽象类自带隐式绑定风险
抽象类可以有 protected 字段、可重写的普通方法、构造器,甚至初始化顺序控制。这些都会把子类拖进不该承担的上下文中:
- 加一个 logger 字段,所有子类都得继承这个状态,哪怕它根本不需要日志
- 提供一个 formatLog() 默认方法,子类重写时可能破坏原有语义或线程安全假设
- 测试时必须构造完整继承链,Mock 成本高;而接口只需 mock 方法返回值即可
接口支持正交组合,抽象类锁定继承路径
一个类可以同时实现 UserRepository、Cacheable、Auditable——说明它在数据访问、缓存、审计三个维度上各自履约。这种能力互不干扰:
- FileUserRepo 和 RedisUserRepo 都实现 UserRepository,上层代码无感知
- 若它们都继承 BaseUserRepo 抽象类,就得共享它的字段、生命周期和初始化逻辑
- 换存储方案时,不是替换实现,而是改继承结构,耦合度陡增
接口命名直指调用方需求
接口名天然以动词或能力为中心:Validator、Serializer、Flyable——每个都在回应“我需要你具备什么能力”。抽象类名则容易滑向“这是一个什么东西”:
- AbstractJsonProcessor 暗示了技术选型,后续想支持 XML 就得重构基类
- BasePaymentHandler 听起来像银联专用,但实际要接入 PayPal 时就会卡住
- 新增 Cacheable 接口,不影响现有契约;加到抽象类里,往往意味着发版、通知、适配

















