不允许此关键字访问记录构造函数中的字段

Ili*_*dis 5 java java-record java-17

我有以下记录:

public record A(List<String> list) {
    public A {
      this.list = new ArrayList<>(list);
    }
}
Run Code Online (Sandbox Code Playgroud)

但我得到这个编译错误:

无法为最终变量赋值

另一方面,这可以编译:

public record A(List<String> list) {
    public A {
      list = new ArrayList<>(list);
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么是这样?

Mar*_*eel 13

原因在JEP 395:记录中给出(强调我的):

可以使用与记录头匹配的形式参数列表显式声明规范构造函数,如上所示。还可以通过省略形式参数列表来更紧凑地声明它。在这样一个紧凑的规范构造函数中,参数是隐式声明的,记录组件对应的私有字段不能在主体中赋值,而是在构造函数末尾自动分配给相应的形参(this.x = x;)。紧凑的形式有助于开发人员专注于验证和规范化参数,而无需将参数分配给字段的繁琐工作。

换句话说,使用紧凑的规范构造函数,您可以更新构造函数参数,然后将该构造函数参数分配给编译器生成的代码中的字段。

这意味着声明

public record A(List<String> list) {
    public A {
        this.list = new ArrayList<>(list);
    }
}
Run Code Online (Sandbox Code Playgroud)

本质上生成:

public record A(List<String> list) {
    public A(List<String> list) {
        this.list = new ArrayList<>(list);
        this.list = list;
    }
}
Run Code Online (Sandbox Code Playgroud)

它尝试两次分配该list字段。Final 字段不允许这样做。

另一方面,以下代码:

public record A(List<String> list) {
    public A {
        list = new ArrayList<>(list);
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

public record A(List<String> list) {
    public A(List<String> list) {
        list = new ArrayList<>(list);
        this.list = list;
    }
}
Run Code Online (Sandbox Code Playgroud)

这完全没问题,因为该list字段仅分配一次。

  • 附带说明一下,为记录字段创建一个新的*可变*“ArrayList”是相当不寻常的。与 `new ArrayList&lt;&gt;(list)` 相比,创建一个不可变的副本更有意义,例如使用 `List.copyOf(list)`,或者,如果需要支持 `null`,则使用 `list.stream().toList ()` (4认同)