Java 14 记录文档中“浅不可变”的含义

Deb*_*was 26 java java-14 java-record

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

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

 R copy = new R(r.c1(), r.c2(), ..., r.cn());  // copy constructor ?
Run Code Online (Sandbox Code Playgroud)

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

Ale*_*x R 30

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

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

  • “浅层不变性”的一个示例可以是具有“ArrayList”实例作为其字段的记录实例。虽然该记录字段无法引用任何其他“ArrayList”列表实例,但列表的元素仍然可以更改(通过保存对记录中包含的列表实例的引用的代码)。因为列表实例仍然是可变的,即使它现在包含在不可变的记录实例中 - 您只能从记录中获得“浅不可变性”。 (5认同)
  • @IvoMori 我严重怀疑情况确实如此,因为这极大地限制了它们的实用性。让这些方法依赖于可变数据并没有什么问题。您只是不能在需要深度不变性的上下文中使用记录类型(例如“Map”ket)。我认为“值”这个词只是指引用,而不是它们背后的对象。 (2认同)

Mic*_*ael 14

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

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

考虑这个类

class Foo {
    private final MutableBar bar;

    //ctor, getter
}
Run Code Online (Sandbox Code Playgroud)

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

foo.getBar().setSomeProperty(5);
Run Code Online (Sandbox Code Playgroud)

所以它并不是一成不变的。

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

class Foo {
    private final int[] ints;

    Foo(int[] ints) {
        this.ints = ints;
    }
}
Run Code Online (Sandbox Code Playgroud)

这可以像这样变异

int[] ints = {1};
Foo foo = new Foo(ints);
ints[0] = 2;
Run Code Online (Sandbox Code Playgroud)

对于一个小的层次结构,有时很容易使浅不可变的类深不可变。它通常涉及防御性副本,或将可变类切换为不可变变体。

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);
    }
}
Run Code Online (Sandbox Code Playgroud)