接口定义能力契约,抽象类封装共享逻辑,具体类通过extends抽象类+implements多个接口实现协作建模;状态字段必须置于抽象类而非接口。

接口与抽象类不是二选一的语法题,而是协作建模的工具组合。关键看你要约束什么、复用什么、留给子类决定什么。
先定契约:用接口声明“必须能做什么”
接口适合定义能力边界,比如所有支付方式都得支持付款和退款,但具体怎么签名、走HTTP还是gRPC,由实现者决定。
- 把行为抽象成方法名,不带字段、不写逻辑,例如:void pay(PayRequest req)、void refund(RefundRequest req)
- 允许添加 default 方法做轻量兼容,比如新版本加个 default void cancel() { ... },老实现不用改也能编译通过
- 多个接口可叠加,比如一个客户端同时 implements AsyncCapable, Retryable, Tracable,每种能力正交独立
再搭骨架:用抽象类封装“大家都一样的部分”
当不同实现共享初始化逻辑、公共字段或固定流程时,抽象类就派上用场。接口做不到的事,它来补。
- 声明 protected 字段,如 protected final HttpClient http、protected final Config config,子类直接用,不用重复声明
- 提供 final 模板方法,比如 public final Response execute(Request r) 固定执行签名→发请求→解析→异常转换,只留 protected abstract HttpRequest buildHttpRequest() 让子类定制
- 强制构造约束,比如抽象类构造器要求传 appKey 和 secret,确保凭证不漏
最后组装:实现类 extends + implements 一起写
一个具体类通常既继承抽象骨架,又实现多个能力接口,这是 Java SDK 的标准写法。
- 语法上必须先 extends(单继承),再 implements(多实现),顺序不能反
- 例如:class WechatPayClient extends AbstractPayClient implements AsyncCapable, NotifySupport
- 抽象类已提供的工具方法(如 hmacSign())可被子类调用;接口的 default 方法可直接使用,也可选择覆盖
避坑提醒:别把状态塞进接口
接口里声明 String seat 看似方便,实际会变成 public static final String seat = null —— 编译报错且违背设计本意。座位类型、车轮尺寸、最大速度这些实例状态,必须放在抽象类里用普通字段承载。










