collections.unmodifiablecollection仅提供只读外壳,不阻断底层集合修改;真正保护aggregate内隐集合需切断原始引用、返回副本或使用list.copyof()等不可变构造方式。

它不能真正保护 Aggregate 内隐集合不被外部修改,只提供一层“禁止调用修改方法”的表层防护。
unmodifiableCollection 是只读外壳,不是数据隔离
该方法返回的只是一个代理对象,内部仍强引用原始集合。只要 Aggregate 内部的 list 或 set 字段仍可被访问(比如通过 getter 暴露、或被其他模块持有引用),外部代码就能绕过 unmodifiableCollection 直接调用 add()、clear() 等方法——而 Aggregate 自身完全无感。
- UnsupportedOperationException 只在你通过不可变视图调用修改方法时抛出
- 若原始集合被第三方代码修改,视图内容实时同步变化,且不报错、不预警
- Aggregate 的不变量(invariants)可能因此被悄然破坏,比如订单项数量与总额不一致
真正保护内隐集合,必须切断原始引用链
领域模型中 Aggregate 的内隐集合(如 Order.items、Customer.addresses)应是封装边界内的私有状态。仅包装不足够,关键在“不让原始引用逃逸”:
- 字段声明为 private final,构造后不再变更引用
- 不提供返回原始集合的 getter(例如不要 public List
- getItems())
- 如需对外暴露,返回新副本:return Collections.unmodifiableList(new ArrayList(items))
- 更稳妥:在 Aggregate 构造或状态变更时,直接用 List.of(items.toArray()) 或 Set.copyOf(items) 创建不可变实例
注意嵌套可变性:子对象仍可被修改
即使 items 列表本身不可变,若其中每个 Item 是可变对象(含 setter、非 final 字段),外部仍可通过 items.get(0).setQuantity(999) 篡改状态。
- unmodifiableList 不递归冻结元素对象
- 若 Item 需不可变,应设计为 immutable 类(所有字段 final + 无 setter + 深拷贝构造)
- 或在返回前做防御性复制:map(item -> new ImmutableItem(item))
推荐替代方案:JDK 10+ 的 copyOf 和 of 系列
相比 unmodifiableCollection,现代 Java 提供更可靠的原生不可变构造方式:
- List.copyOf(items) —— 自动判空、去 null、返回真正不可变 List,且不保留对原集合引用
- Set.copyOf(items) —— 自动去重并冻结,比 unmodifiableSet 安全得多
- List.of(e1, e2) 或 List.of(items.toArray()) —— 创建紧凑、不可变、拒绝 null 的实例
- 注意:of 系列要求元素非 null,且 List.of() 最多 10 个元素;超限时用 List.copyOf()
不复杂但容易忽略:保护 Aggregate 内隐集合,核心不在“加锁”或“包装”,而在“封装意识”——让集合成为真正的私有实现细节,而非可传递的共享引用。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











