优先不可变是什么
把能不变的变量都声明为不可变:缩小可变状态的范围,审查时一眼分出哪些值会变。它与拆分变量、以查询取代临时变量协同使用,配合编译器的只读检查,副作用面会持续收窄,推理代码时需要考虑的状态组合也随之减少。
基本信息
| 常用手段 | const / final / val / readonly |
|---|---|
| 推进顺序 | 局部变量 → 字段 → 集合内容 |
| 收益 | 变更点集中,代码推理更简单 |
| 相关手法 | 拆分变量、以查询取代临时变量 |
| 语言差异 | Rust 绑定默认不可变,Java 需要显式修饰 |
典型使用场景
- 局部计算结果只在初始化时赋值一次
- 构造函数注入的依赖在对象生命周期内不变
- 配置对象在启动加载完成后只读
- 常量集合对外只读暴露
- 函数参数按只读方式使用,避免意外回写
- 审查时先标出会变的状态,其余按不可变处理
三种语言对照
三种语言对照
// JavaScript:优先 const,避免中途重新赋值
const total = items.reduce((sum, item) => sum + item.price, 0);
// Java:局部变量与字段尽量 final
final BigDecimal total = computeTotal(items);
// Rust:绑定默认不可变,需要修改才写 mut
let total = items.iter().map(|i| i.price).sum();不可变的三个层次
| 层次 | 做法 | 说明 |
|---|---|---|
| 声明 | 加不可变修饰符 | 局部变量先行,成本最低 |
| 字段 | 构造后不再赋值 | 需要显式设计,避免依赖懒初始化 |
| 集合内容 | 只读视图或不可变集合 | 防止元素被外部修改 |
不可变不等于性能差:多数语言对不可变绑定有优化。真正需要评估的是把大对象复制成新实例,而不是禁止重新赋值。
集合声明为不可变只是浅不可变:集合元素本身仍可能被修改。共享出去之前要确认返回的是只读视图还是副本。
先把不可变写足再谈优化:编译期能确定的不变量交给类型系统承担,运行期需要判断的状态就会变少,测试要覆盖的组合数量也随之下降。