里氏替换原则要求子类能安全替代父类且调用方无需修改:需严格遵循“is-a”语义、用接口替代伪继承、守好方法契约三要素(前置不加强、后置不削弱、异常不扩大),违例时应重构为组合或接口实现。

里氏替换原则(LSP)不是靠“加个注解”或“写个文档”实现的,而是通过设计决策和代码契约来落实。核心是:**子类必须能安全替代父类,且调用方无需修改、无需判断、无需降级处理**。
明确继承边界:只对真正满足“is-a”且行为一致的场景用继承
继承不是为了复用代码而存在,而是为了表达语义上的“是一种”。比如:
-
合理:
ArrayList和LinkedList都是List的具体实现——它们都支持按索引访问(尽管效率不同)、顺序遍历、增删改查等契约行为; -
不合理:让
Ostrich继承含fly()方法的Bird类——因为“鸵鸟是一种鸟”成立,但“鸵鸟能飞”不成立,破坏了父类方法的行为承诺。
用接口+实现替代“伪继承”
当类型之间只有部分能力重叠,而非全貌兼容时,优先定义行为接口,再由各类型按需实现:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 把通用能力拆成接口,如
Flyable、Swimmable、LayEggs; -
Sparrow implements Flyable,Ostrich不实现Flyable,但可实现RunFast; - 客户端按需依赖接口:
void launch(Flyable flyer),自然排除无法飞行的类型,无需instanceof判断。
严格遵守方法契约三要素
子类重写父类方法时,必须守住以下底线:
-
前置条件不加强:父类方法接受
int x,子类不能要求x > 0并抛异常; -
后置条件不削弱:父类承诺返回非
null列表,子类不能返回null或空集合(除非父类契约允许); -
异常不扩大:父类声明
throws IOException,子类可不抛异常,或只抛FileNotFoundException(子类异常),但不可新增SQLException。
重构违反 LSP 的继承结构
发现子类大量抛 UnsupportedOperationException、或客户端频繁做类型检查时,就是重构信号:
- 把原父类中“并非所有子类都具备”的方法移出继承链;
- 将原父类改为抽象基类或接口,仅保留真正共有的不变量与契约;
- 用组合替代继承:例如
Bird持有FlightBehavior字段,Ostrich注入NullFlightBehavior,Eagle注入SoaringBehavior。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










