使用不可变对象模拟属性更改



搜索后,我找到了这些问题,但没有回答我的具体问题:

使用不可变对象撤消/重做

为什么要继续使用不可变对象的 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类和原始的装箱类型IntegerDouble等,被写成是不可变的。

您的变量声明

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

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

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

最新更新