接口优于抽象类,因其零状态、零实现、聚焦行为契约;抽象类易引入状态和实现细节,导致耦合与继承污染。

因为接口只回答“能做什么”,不掺杂“是什么”或“怎么做”的暗示;抽象类天然携带状态、构造逻辑和实现细节,容易让契约被悄悄污染。
接口强制零状态、零实现
接口里不能定义普通字段、不能写构造方法、不能有 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 接口,不影响现有契约;加到抽象类里,往往意味着发版、通知、适配
守住接口边界,就是守住解耦的底线。











