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

this-keyword is not allowed to access a field in constructors of records

我有以下记录:

public record A(List<String> list) {
    public A {
      this.list = new ArrayList<>(list);
    }
}

但是我得到这个编译错误:

Cannot assign a value to final variable

另一方面,编译:

public record A(List<String> list) {
    public A {
      list = new ArrayList<>(list);
    }
}

这是为什么?

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

The canonical constructor may be declared explicitly with a list of formal parameters which match the record header, as shown above. It may also be declared more compactly, by eliding the list of formal parameters. In such a compact canonical constructor the parameters are declared implicitly, and the private fields corresponding to record components cannot be assigned in the body but are automatically assigned to the corresponding formal parameter (this.x = x;) at the end of the constructor. The compact form helps developers focus on validating and normalizing parameters without the tedious work of assigning parameters to fields.

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

这意味着声明

public record A(List<String> list) {
    public A {
        this.list = new ArrayList<>(list);
    }
}

实质上生成:

public record A(List<String> list) {
    public A(List<String> list) {
        this.list = new ArrayList<>(list);
        this.list = list;
    }
}

尝试分配 list 字段两次。这对于最终字段是不允许的。

另一方面,以下代码:

public record A(List<String> list) {
    public A {
        list = new ArrayList<>(list);
    }
}

结果:

public record A(List<String> list) {
    public A(List<String> list) {
        list = new ArrayList<>(list);
        this.list = list;
    }
}

这很好,因为 list 字段只分配了一次。

当你提到this.list时,它意味着当前object/class的属性,并且在记录中所有道具都是最终的

类似于

final String s = "ABC";

如果你现在试试

s = "PQR";

它会给你同样的错误,因为 's' 是最终的

public record A(List<String> list){
    public A{
      this.list=new ArrayList<>(list);
    }
}

虽然你可以这样做

public record A(List<String> list){
    public A(List<String> list){
      this.list=new ArrayList<>(list);
    }
}

records 的所有属性都是最终的,您不能将值重新分配给 record,这是使用 records

的原因之一

如果您想重新分配值,请使用 classes

你在第二部分没有得到错误的原因是你正在创建紧凑的构造函数,你真正应该做的是

public record A(List<String> list){
    public A(List<String> list){
      this.list=new ArrayList<>(list);
    }
    public A{
      if(!Objects.nonNull(list){
        throw new IllegalArgumentException("List must not be empty")
      }
    }

}

换句话说,当您初始化新记录时,它只会将该参数值设置为其属性,当您尝试使用此关键字重新分配时,它肯定会抛出错误,因为它的属性是最终的

对于第二个代码块,您只是为参数赋值 'list' 您没有更改 class 属性

我希望这能回答你的问题。

了解更多详情go here