LSP 是面向对象设计五大原则SOLID中的核心之一强调的是行为上的可替换性而非语法上的禁止重写。关键在于✅ 子类可以重写父类方法如override但必须保证前置条件输入约束不增强子类方法不能要求比父类更严格的参数例如父类接受int子类不能只接受正整数后置条件输出/副作用不削弱子类方法返回值、异常类型、状态变更等需符合或强于父类承诺例如父类保证不抛出异常子类也不能抛不变量invariants必须保持子类对象在任何时刻都应满足父类所定义的状态约束。这本质上是契约式设计Design by Contract的体现——父类定义了“接口契约”子类必须遵守而非仅仅实现签名。# ✅ 符合 LSP 的示例classBird:deffly(self)-str:returnFlying highclassSparrow(Bird):# 正常继承并重写语义一致deffly(self)-str:returnFlying swiftlyclassOstrich(Bird):# ❌ 违反 LSP若 fly() 抛出 NotImplementedError则用 Bird 引用调用时行为突变deffly(self)-str:raiseNotImplementedError(Ostriches dont fly)不一定一定违反里氏替换原则但绝大多数情况下会违反关键取决于该异常是否属于父类契约所允许的“后置条件”范围。根据 LSP 的核心精神基于契约的设计父类方法的异常类型是其接口契约的重要组成部分。Barbara Liskov 原始定义中明确指出子类方法抛出的异常类型应是父类方法所声明异常的子类型即更具体而非新增未声明的、更宽泛或无关的异常类型。✅不违反的情况罕见但合法父类方法在契约中隐式或显式允许该异常例如文档注明“可能因外部服务不可用而失败”且新异常NetworkError是该语义的合理细化父类使用宽松异常类型如Exception或自定义基类ServiceError子类抛出其子类异常如TimeoutError继承自ServiceError——这属于增强约束符合“后置条件不削弱”该异常是运行时底层异常如MemoryError属于系统级不可控错误不属于业务契约范畴通常可豁免但不应主动引入。❌典型违反情况父类方法签名/文档/约定中未提及、未承诺、也未预留某种业务异常如InsufficientBalanceError而子类突然抛出——调用方若只捕获父类预期异常就会导致未处理异常崩溃行为不可预测子类将原本静默失败或返回默认值的父类方法改为强制抛出新异常如父类save()返回bool表示成功/失败子类改为save()抛出ValidationError——改变了调用方的错误处理契约。 本质判断标准“用父类引用指向子类对象并以父类契约方式调用程序逻辑是否仍能正确、安全、可预期地运行”若新增异常导致原有错误处理失效、流程中断或语义误解即违反 LSP。