里氏替换原则要求子类对象能安全替换父类对象且行为不变;核心是子类不得破坏父类契约,需满足前置条件不增强、后置条件不削弱、不变量守恒等准则。

里氏替换原则(Liskov Substitution Principle,简称 LSP)是 Java 面向对象设计中六大核心原则之一,它的核心就一句话:子类对象必须能够替换父类对象,且程序行为不发生改变。换句话说,只要代码里用到了父类类型的地方,换成它的任意合法子类实例,运行结果应该完全一致、不出错、不反逻辑。
它解决什么问题
继承不是“能编译通过”就万事大吉。现实中常出现:父类方法语义清晰(比如 setHeight() 只改高不碰宽),子类一重写却悄悄联动修改了宽——外部调用者按父类契约使用,结果出 bug。LSP 就是给继承划一条底线:子类不能偷偷破坏父类已承诺的行为规则。
常见违反场景包括:
- 子类重写父类非抽象方法时,改变了原逻辑(如把“计算原价”改成“返回折扣价”)
- 子类覆盖父类方法后,对入参做了更严格的校验(父类允许 null,子类拒绝 null)
- 子类重写后返回类型变宽松(父类返回
ArrayList,子类返回List看似合理,但若调用方用了ArrayList特有方法就会崩) - 用正方形继承矩形:父类有
setWidth()和setHeight(),子类重写后两个方法都强制同步边长——这违背了矩形“宽高可独立设置”的隐含契约
怎么才算合规
关键不是“能不能继承”,而是“替换了是否还稳得住”。判断依据很实在:
立即学习“Java免费学习笔记(深入)”;
- 前置条件不增强:子类方法的输入约束不能比父类更严(比如父类接受任意正整数,子类不能只接受 1~100)
- 后置条件不削弱:子类方法的输出结果、异常范围、副作用等,不能比父类承诺得更少或更松(比如父类保证返回非 null,子类就不能返回 null)
- 不变量要守住:父类定义的业务不变量(如“余额不能为负”“ID 一旦设置不可变”),子类必须同样维护
-
不要覆盖已有实现:对已有具体方法,优先新增方法(如
getDiscountPrice()),而不是重写getPrice()改含义
实际开发中的提醒
LSP 不是理论清规戒律,而是帮你提前避坑的实践指南:
- 写父类时,把方法契约写清楚(JavaDoc 注明参数范围、返回含义、可能异常)
- 写子类前,先问一句:“如果现在所有父类变量都换成我这个子类,现有测试还全过吗?”
- 发现子类很难“干净”继承时,别硬扛——考虑用组合(has-a)代替继承(is-a),比如让“正方形”持有“边长”字段,而不是继承“矩形”
- 单元测试里,可以用子类实例直接替换父类参数做回归验证,这是检验 LSP 最直接的方式
基本上就这些。它不复杂,但容易忽略;守住了,继承才真正成为助力,而不是埋雷工具。










