28

我正在阅读Records的文档, 但不理解“浅不可变”一词。浅不可变是什么意思?如果它是不可变的,为什么我们需要一个复制构造函数?为什么是两个“Hello Worlds!”?

对于所有记录类,以下不变量必须保持:如果记录 R 的组件是 c1、c2、...cn,那么如果记录实例被复制如下:

 R copy = new R(r.c1(), r.c2(), ..., r.cn());  // copy constructor ?

那么它一定是这样的r.equals(copy)

4

2 回答 2

32

浅不可变意味着,如果一个类有字段,则这些字段被视为final. 但是,它们的字段(即字段的字段)不需要是final.

您不需要实现构造函数,它已经为您实现了这种方式。但是如果您选择自己实现它,例如用于参数验证,那么这个不变量应该成立。

于 2020-05-28T05:03:15.933 回答
14

如果您将一个类视为其他类和基元(整数、数组等)的组合或层次结构,那么浅不变性是指仅第一级的不变性(恒定性)。

它与术语“深度不变性”形成对比,后者指的是整个层次结构的不变性。您听到的关于不可变性的大多数有形好处,例如隐式线程安全,仅适用于深度不可变的东西。

考虑这个类

class Foo {
    private final MutableBar bar;

    //ctor, getter
}

这个类是浅不可变的。它不能直接改变,但可以间接改变,例如

foo.getBar().setSomeProperty(5);

所以它不是深度不变的。

另一个浅不变性的例子,只使用原语

class Foo {
    private final int[] ints;

    Foo(int[] ints) {
        this.ints = ints;
    }
}

这可以像这样变异

int[] ints = {1};
Foo foo = new Foo(ints);
ints[0] = 2;

对于较小的层次结构,有时将浅不可变的类设为深度不可变很简单。它通常涉及防御性副本,或将可变类转换为不可变的变体。

class Foo {
    private final int[] ints; 

    Foo(int[] ints) {
        // copy to protect against the kind of mutation shown above
        this.ints = Arrays.copyOf(ints, ints.length);
    }

    // if you must have a getter for an array, make sure not to return the array itself, 
    // otherwise the caller can change it.
    // for performance reasons, consider an immutable List instead - no copy required
    int[] getInts() {
        return Arrays.copyOf(ints, ints.length);
    }
}
于 2020-05-28T13:44:56.790 回答