子类覆盖父类 setter 的核心是保持契约:语义一致、前置条件不收紧、后置效果可预期;应优先组合 super 调用并增量增强,避免绕过父类逻辑,且须通过原有单元测试验证兼容性。

子类覆盖父类 setter 时,核心不是“要不要改”,而是“怎么改才不破坏原有契约”。关键在于保持语义一致、前置条件不收紧、后置效果可预期——尤其当 setter 承担校验、联动或状态同步职责时。
明确 setter 的隐含责任
父类 setter 往往不只是赋值,还可能附带以下逻辑:
- 参数合法性检查(如非空、范围限制)
- 触发属性变更通知(如 PropertyChangeListener)
- 联动更新其他字段(如 setWidth() 同时重算面积)
- 记录修改时间戳或审计日志
- 抛出特定异常(如 IllegalArgumentException)
子类重写前,先看父类 setter 实现;若没源码,至少读 Javadoc 或跑单元测试确认行为边界。
推荐做法:组合 super 调用 + 增量增强
多数安全场景下,应保留父类校验和基础赋值,再叠加子类特有逻辑:
- 用 super.setName(name) 完成原始校验与赋值
- 在 super 调用前后插入子类逻辑:前置预处理(如标准化输入)、后置响应(如刷新缓存)
- 避免绕过 super 直接操作字段——否则会跳过父类约束,导致对象状态不一致
示例:Cat 类增强 setName
public void setName(String name) {if (name == null) throw new IllegalArgumentException("猫名不可为空");
super.setName("Cat: " + name); // 复用父类非空校验+赋值
this.lastModified = System.currentTimeMillis(); // 子类独有行为
}
警惕“覆盖即重写全部”的误区
以下情况不适合直接覆盖 setter:
- 父类 setter 是 final —— 此时无法覆盖,需通过组合或委托实现扩展
- 子类语义与父类根本冲突(如父类 setPrice() 允许负数表示折扣,子类却强制 >0)—— 这违反里氏替换,应重构为接口或抽象基类
- setter 被多个子类以不同方式增强,造成重复代码——考虑提取模板方法,或用策略模式解耦校验/响应逻辑
若必须差异化行为,优先在父类中预留钩子(如 protected void onNameChanged(String old, String new)),让子类选择性重写钩子而非整个 setter。
验证是否真正兼容
仅靠编译通过不够。要确保:
- 父类的 setter 单元测试(含边界值、非法输入)在子类实例上仍全部通过
- 调用方代码不改一行,换用子类对象后,业务流程无异常、无静默失败
- 日志、监控、序列化等横切关注点行为不变或按预期演进
自动化测试是唯一可靠手段。没有测试覆盖的 setter 覆盖,本质上是埋雷。










