当你写一个构造函数(或任何方法),它接收一个可变对象(Mutable Object)作为参数,并且要把这个对象存下来作为你类的一部分(即存入一个字段/成员变量),你就必须创建它的防御性拷贝(Defensive Copy)。
在考试时,看到一个构造函数,问自己这两个问题:
-
参数是不是一个“盒子”?
- 参数的类型是不是一个可以被修改的集合(如
Collection,List,ArrayList,Map,HashMap)或者其他可变对象(如Date,StringBuilder)? - 原始类型(
int,double,boolean)和不可变类型(String,Integer)不算。它们天生是安全的。 - 在你的代码
public CombinedPolicy(Collection<...> policies)中,Collection就是一个“盒子”,所以第一个问题的答案是**“是”**。
- 参数的类型是不是一个可以被修改的集合(如
-
我是不是要把这个“盒子”存起来以后用?
- 你是不是在构造函数里写了
this.fieldName = aParameter这样的代码? - 在你的代码
this.policies = List.copyOf(policies);中,你显然是要把这个policies集合存到this.policies字段里,供以后(比如traversable()方法)使用。所以第二个问题的答案也是**“是”**。
- 你是不是在构造函数里写了
如果两个问题的答案都是“是”,那么你就必须使用 List.copyOf(或 Map.copyOf 等)或者 new ArrayList<>(...) 这样的方式来创建一个副本。
为了防止创建你对象的“客户”代码,在创建完对象之后,还能偷偷修改你对象内部的状态。
List.copyOf 是最好的选择,因为它不仅创建了副本,还保证了你内部的这份副本是不可修改的,提供了双重保险。
简单总结: 在构造函数里,凡是要存起来的、可变的参数,都要复制一份。这应该成为你的肌肉记忆。