0

0

里氏替换原则 (LSP)_继承关系中子类的正确行为规范

P粉602998670

P粉602998670

发布时间:2026-03-02 11:46:41

|

374人浏览过

|

来源于php中文网

原创

子类重写父类非抽象方法极大概率违反lsp,因客户端依赖父类方法契约而非实现;行为变更(如新增异常、收紧前置条件、改变副作用)会导致调用方逻辑崩溃。

里氏替换原则 (lsp)_继承关系中子类的正确行为规范

子类重写父类非抽象方法,为什么程序会悄悄出错

直接结论:只要子类覆盖了父类已实现的(非 abstract)方法,就极大概率违反 LSP —— 因为客户端代码依赖的是父类方法的契约,不是它的具体实现。一旦子类偷偷改了行为(比如 withdrawal() 从“只扣款”变成“先校验再冻结账户”),所有用 CashCard 类型声明的地方,换成 CreditCard 实例后,逻辑就可能崩。

  • 常见错误现象:c.withdrawal("1001", new BigDecimal("500")) 在父类返回 "0000",子类却抛出 InsufficientCreditException,而调用方根本没准备捕获这个异常
  • 本质是契约断裂:父类方法签名承诺“成功返回码”,子类却引入新失败路径,或改变前置条件(如要求 amount > 0 变成 amount >= 100
  • Java 中最隐蔽的坑:@Override 一个看似无害的 toString()equals(),结果子类用了不同字段参与比较,导致 HashSet 查不到对象

正方形不能继承长方形?这不是数学问题,是接口语义问题

不是“正方形是不是长方形”的几何争论,而是 RectanglesetWidth()setHeight() 方法隐含了“彼此独立可变”的契约。子类 Square 把二者绑死,就破坏了这个语义——任何依赖“设宽不影响高”的代码,一换就错。

  • 典型使用场景:UI 布局引擎接收 Shape 列表,循环调用 setWidth(200) 调整控件宽度,若其中混入 Square 实例,高度也被强制拉成 200,界面直接错乱
  • 正确解法不是“让正方形更像长方形”,而是退一步:用接口(Shape)或组合(Square 持有 Rectangle 并代理部分行为)代替继承
  • 参数差异很关键:父类 setHeight(int h) 允许任意整数,子类若只接受偶数,就是前置条件收紧,违反 LSP;反过来,子类接受 Number 而非 int,才叫放宽(合法)

如何快速判断一个继承关系是否踩了 LSP 红线

别靠直觉,用三句话拷问自己:

凡科AI抠图
凡科AI抠图

简单好用的在线抠图工具

下载
  • 有没有地方把父类对象换成子类后,**不改一行调用代码**,结果就变了(返回值、异常、副作用、执行时间)?
  • 子类有没有重写父类的非 abstract 方法?如果有,它是否**完全保持原输入输出规则、不变量、文档承诺**?
  • 子类新增的方法,会不会诱使调用方写出“先检查是不是子类,再调用特有方法”的脆弱代码?(这说明继承关系本就不该存在)

工具上,IDE 的 “Find Usages” 配合手动替换测试最有效:把 Rectangle r = new Rectangle(); 改成 Rectangle r = new Square();,跑一遍所有用到 r 的单元测试,挂掉的就是 LSP 雷区。

PHP/Python/Java 中容易被忽略的 LSP 细节

语言特性会放大 LSP 风险,尤其在动态类型或弱契约环境下:

  • PHP 中 function setWidth(int $w) 和子类 function setWidth($w)(去掉类型声明)看似兼容,但实际放宽了输入,如果父类内部做了 is_int() 校验,子类就可能绕过,造成后续逻辑崩溃
  • Python 的鸭子类型不等于 LSP 自动成立:class Birdfly()class Ostrich(Bird) 重写为 raise NotImplementedError,看似“有方法”,但任何遍历 [Bird(), Ostrich()] 并调用 .fly() 的代码都会炸
  • Java 的协变返回值(子类方法返回父类方法返回类型的子类型)是安全的,但逆变参数(子类方法参数是父类方法参数的父类型)必须显式声明,否则编译器不会帮你拦住

最常被跳过的点:LSP 不是关于“能不能编译通过”,而是关于“换掉之后,运行时行为是否还稳”。很多团队只测 happy path,漏掉了子类替换后边界条件、并发、异常流的验证。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

910

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

596

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

294

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

210

2025.08.29

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1732

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

549

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2338

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

43

2026.01.19

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

48

2026.02.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号