搜索后,我找到了这些问题,但没有回答我的具体问题:
使用不可变对象撤消/重做
为什么要继续使用不可变对象的 getter
如何编写测试友好的不可变值类
是否可以公开不可变对象的状态
这是设置:我有一个不可变的类,如下所示:
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);
}
我很难解决这个问题。
正如你所发现的,为了使类的实例不可变,你必须显式地将不可变性写入你的类;对象类型(与基元类型相反)本质上是可变的。java.util
中的集合类当然是可变的。像String
这样的Java类和原始的装箱类型Integer
、Double
等,被写成是不可变的。
您的变量声明
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
的使用,因为当它在MyImmutableOb
中不可变时,使用该专用List
实现是没有意义的。
因此,您的第二个构造函数成为
public MyImmutableOb(String name, MyImmutableOb oldOb) {
this.name = name;
this.myNumbers = oldOb.myNumbers;
}
因为oldOb
的myNumbers
已经是不可变的,所以复制是没有意义的。
最后,我意识到您可能确实希望在构建MyImmutableOb
后能够更改myNumbers
的内容。显然,使类真正不可变意味着在构造后,在其实例中没有任何内容可以更改。