使用不可变对象模拟 属性 变化
Simulating property changes with immutable objects
搜索后我发现了这些问题,但没有找到我的具体问题的答案:
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
设置如下:
我有一个不可变的 class 像这样:
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);
}
我很难理解这个问题。
正如您所发现的,为了使 class 不可变 的实例,您必须明确地将不可变性写入 class;对象类型(相对于原始类型)本质上是 可变的。 java.util
中的集合 classes 当然是可变的。 Java class 像 String
和原始拳击类型 Integer
、Double
等,被写成不可变的。
你的变量声明
private final String name;
是不可变的,因为它被声明为 final
和 因为 String
对象是不可变的。
对变量使用final
关键字可确保变量只能赋值一次,因此它是设计不可变class的绝佳工具。然而,这还不够,因为如果变量被分配给一个可变对象,那么 that 对象中的变量仍然可以被修改。
你的变量声明
private final Collection<int> myNumbers;
不保证不变性,因为尽管 myNumbers
只能分配一次,但分配给它的集合仍然是可变的。
好消息是,Java Collections
实用程序 class 提供轻量级包装器,使您的集合不可变。这将需要更改您的第一个构造函数
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
的内容。显然,使 class 真正不可变意味着构造后其实例中的任何内容都无法更改。
搜索后我发现了这些问题,但没有找到我的具体问题的答案:
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
设置如下: 我有一个不可变的 class 像这样:
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);
}
我很难理解这个问题。
正如您所发现的,为了使 class 不可变 的实例,您必须明确地将不可变性写入 class;对象类型(相对于原始类型)本质上是 可变的。 java.util
中的集合 classes 当然是可变的。 Java class 像 String
和原始拳击类型 Integer
、Double
等,被写成不可变的。
你的变量声明
private final String name;
是不可变的,因为它被声明为 final
和 因为 String
对象是不可变的。
对变量使用final
关键字可确保变量只能赋值一次,因此它是设计不可变class的绝佳工具。然而,这还不够,因为如果变量被分配给一个可变对象,那么 that 对象中的变量仍然可以被修改。
你的变量声明
private final Collection<int> myNumbers;
不保证不变性,因为尽管 myNumbers
只能分配一次,但分配给它的集合仍然是可变的。
好消息是,Java Collections
实用程序 class 提供轻量级包装器,使您的集合不可变。这将需要更改您的第一个构造函数
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
的内容。显然,使 class 真正不可变意味着构造后其实例中的任何内容都无法更改。