里氏替换原则要求子类对象能安全替换父类对象而不改变程序行为,核心是保持父类契约(功能语义、前置/后置条件、不变量),而非仅语法正确;需通过设计约束、行为一致性和黑盒测试验证。

里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计的五大原则(SOLID)之一,核心意思是:子类对象必须能够替换父类对象,且程序行为不发生改变。在 Java 继承中遵循 LSP,不是靠语法强制,而是靠设计约束和编码自觉。
用“行为一致”代替“语法正确”
Java 允许 extends 任意类、重写任意非 final 方法,但这不等于符合 LSP。关键看子类是否保持了父类定义的契约(contract)——包括方法的功能语义、前置条件、后置条件和不变量。
- 父类方法声明“查找用户”,子类不能重写为“创建并返回新用户”
- 父类约定“传入 null 抛 IllegalArgumentException”,子类不能静默接受 null 或抛 NullPointerException
- 父类保证“返回非 null 列表”,子类不能返回 null
避免破坏父类不变量
父类内部维护的状态约束(如 “balance ≥ 0”、“status 为 OPEN 或 CLOSED”)必须在子类所有操作后依然成立。
- 不要在子类中暴露或修改父类私有状态字段(哪怕通过反射)
- 重写 setter 或业务方法时,先校验再变更,确保不变量不被绕过
- 构造器中避免调用可被重写的方法(防止子类初始化未完成就触发重写逻辑)
谨慎使用重写,优先考虑组合与接口
很多 LSP 违反源于强行继承。例如:让 Ostrich(鸵鸟)继承 Bird(并重写 fly() 为空),就违背了“Bird 可飞”的隐含契约。
立即学习“Java免费学习笔记(深入)”;
- 把“is-a”关系换成“has-a”或“can-do”:用
Flyable接口 + 实现类更安全 - 若必须继承,确保子类是父类的严格行为加强版(如缩小参数范围、增强返回值约束),而非削弱
- 对关键方法加
@Override注解,并在 Javadoc 明确说明是否扩展/收紧契约
用单元测试验证替换可行性
为父类写一组黑盒测试(只依赖 public API),然后让子类直接运行同一套测试。全部通过,才是真正的 LSP 合规。
- 测试应覆盖正常输入、边界值、异常路径(如空参、负数、已关闭状态等)
- 避免测试子类特有方法——那是子类自己的职责,不是替换的前提
- 若子类无法通过某项父类测试,说明它不该继承该父类
不复杂但容易忽略:LSP 不是关于能不能编译,而是关于敢不敢在不改任何调用代码的前提下,把 new Parent() 换成 new Child()。


















