java多态的稳定性依赖行为契约的严格执行而非语法糖,里氏替换原则是其安全前提;接口需明确定义输入范围、返回语义、异常类型等细节,实现类须忠实履行且不可收窄前置条件或弱化后置条件。

Java 多态的稳定性不靠语法糖,而靠行为契约的严格执行。里氏替换原则(LSP)不是多态的“附加要求”,而是它能安全落地的前提——只有当子类真正可替代父类时,用父类引用调用方法才不会出错。
接口/抽象类必须定义清晰的行为契约
仅声明方法签名远远不够。契约要覆盖输入范围、返回语义、异常类型、null 策略、线程安全等细节:
- 比如 void save(User user) 的契约应明确:user 为 null 时抛 IllegalArgumentException;实现类不能静默忽略,也不能改成返回 boolean
-
List
findAll() 应约定“永不返回 null,空结果返回不可变空列表”;若某个实现返回 null,调用方 NPE 就是 LSP 破坏的直接后果 - 避免模糊方法名如 process() ——没有契约约束,各实现自由发挥,多态就变成“猜行为”
实现类必须忠实履行契约,不能收窄输入、不能弱化输出
子类不是父类的“加严版”或“缩水版”,而是行为上更宽或更强的版本:
- 前置条件只能放宽:接口要求 id > 0,实现类可接受 id ≥ 0;但若要求 id > 100,就可能让原本合法的调用失败
- 后置条件只能增强:接口承诺“返回非 null 列表”,实现类返回 Collections.unmodifiableList(...) 是合规的;返回 null 或可变空列表则违反 LSP
- 异常策略必须兼容:接口未声明受检异常,实现类不得新增;已声明 IOException,实现类可抛 FileNotFoundException(子类),但不能抛 RuntimeException(破坏调用方预期)
避免语义冲突的接口组合,用组合代替强行实现
一个类不该为了“看起来全能”而实现互相矛盾的接口:
- Ostrich 实现 Flyable 却在 fly() 中 throw UnsupportedOperationException,调用方按契约调用必崩——这不是多态,是陷阱
- 正确做法是拆分职责:只让 Eagle、Sparrow 实现 Flyable;Ostrich 只实现 Walkable;或把飞行能力抽成 FlightBehavior,由具体类型注入
- 拒绝 instanceof 判断或强制转型来绕过接口——那是设计缺陷的补丁,不是多态的解决方案
泛型限定与聚合接口提升替换安全性
当方法需要对象具备多种能力时,优先用类型参数约束,而非让类实现一堆接口:
- 写
& Serializable> ,比让 User 同时实现 Comparable 和 Serializable 更灵活、更安全 - 对复杂能力组合,用小接口聚合:定义 Readable & Writable & Closeable 组合接口,再让具体类选择性实现,避免大而全的“上帝接口”
- 这样既保持多态粒度,又让替换关系更明确、更可控
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











