使用不可变对象模拟 属性 变化

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 和原始拳击类型 IntegerDouble 等,被写成不可变的。

你的变量声明

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;
}

因为oldObmyNumbers已经是不可变的,所以复制没有意义。

最后,我意识到您可能确实希望能够在构建 MyImmutableOb 之后更改 myNumbers 的内容。显然,使 class 真正不可变意味着构造后其实例中的任何内容都无法更改。