搜索后我发现了这些问题,但没有回答我的具体问题:
Undo/Redo with immutable objects
Why continue to use getters with immutable objects
How to write a test friendly immutable value class
Is it okay to expose the state of an immutable object
以下是设置: 我有一个像这样的不可变类:
public class MyImmutableOb {
private final String name;
private final Collection<Integer> myNumbers;
public MyImmutableOb(String name) {
this.name = name;
myNumbers = new LinkedList<>();
}
public MyImmutableOb(String name, MyImmutableOb oldOb) {
this.name = name;
myNumbers = new LinkedList<>();
for (int i : oldOb.getNumbers()) myNumbers.add(i);
}
public Collection<Integer> getNumbers() {
return new LinkedList<>(myNumbers);
}
}
我已经包含了这样的构造函数,意味着允许模拟对象的名称更改。问题是,以这种方式复制Collection
是否会破坏不变性?:
public MyImmutableOb(String name, MyImmutableOb oldOb) {
this.name = name;
myNumbers = new LinkedList<>();
for (int i : oldOb.getNumbers()) myNumbers.add(i);
}
我正在艰难地围绕着这个问题。
答案 0 :(得分:2)
正如您所发现的,为了使您的类的实例不可变,您必须明确地将不可变性写入您的类;对象类型(与原始类型相对)本质上是可变的。 java.util
中的集合类当然是可变的。像String
和原始装箱类型Integer
,Double
等Java类被编写为不可变的。
您的变量声明
private final String name;
是不可变的,因为它被声明为final
和,因为String
个对象是不可变的。
对变量使用final
关键字可确保只为变量赋值一次,因此它是设计不可变类的绝佳工具。但是,这还不够,因为如果将变量分配给可变对象,那么 对象中的变量仍然可以被修改。
您的变量声明
private final Collection<int> myNumbers;
不保证不变性,因为虽然myNumbers
只能分配一次,但分配给它的集合仍然是可变的。
好消息是,Java Collections
实用程序类提供了轻量级包装器,使您的集合不可变。这将需要更改您的第一个构造函数
public MyImmutableOb(String name, int... numbers) {
this.name = name;
this.myNumbers = Collections.unmodifiableList(Arrays.asList(numbers));
}
这样myNumbers
现在真正不可变。请注意,我已经放弃了对LinkedList
的使用,因为当List
中的MyImmutableOb
不可变时,使用该专门的public MyImmutableOb(String name, MyImmutableOb oldOb) {
this.name = name;
this.myNumbers = oldOb.myNumbers;
}
实现是没有意义的。
因此你的第二个构造函数变为
oldOb
因为myNumbers
的{{1}}已经是不可变的,所以复制没有意义。
最后,我意识到你可能确实希望在构建myNumbers
之后能够更改MyImmutableOb
的内容。显然,使类真正不可变意味着在构造之后其实例中不会发生任何变化。