为什么在紧凑构造器中调用记录访问器会返回默认字段值?

编程语言 2026-07-12

我在尝试使用Java的 record(记录类型)和紧凑构造器。

给定这个记录:

public record Point(double x, double y) {

    public Point {
        if (x > y) {
            System.out.println(this.x());
        }
    }

    public void printXAndY() {
        System.out.printf("X: %.2f; Y: %.2f%n", this.x, this.y);
    }
}

当我创建:

new Point(5.0, 3.0);

输出是:

0.0

然而,如果我直接读取字段:

System.out.println(this.x);

编译器报告一个错误,指出该字段无法被读取。

我的理解是:

  • 在紧凑构造器中,字段是在构造器主体之后被赋值。
  • 因此,在尚未初始化之前读取this.x是不合法的。
  • 但调用this.x() 可以编译通过。
  • 在运行时,它返回0.0,似乎是字段的默认值。

我的问题是,为什么调用访问器方法(this.x())会返回字段的默认值,而不是构造函数参数的值?

解决方案

这是Java的正常行为,对于“普通”类也同样适用。例如,下面作为普通类的等价实现也能编译通过。

public class Point {

    private final int x;
    private final int y;

    public DummyClass(int x, int y) {
        if (x > y) {
            System.out.println(getX());
        }
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    static void main() {
        new Point(5, 4);
    }
}

(注:Java 25 main 不再需要 public 也不需要 String[] 参数。)

从编译器的角度来看,进行的检查是“不完整的”。对于方法调用(在你的问题中是 this.x(),在我的示例中是 getX()),编译器仅检查该方法的返回类型是否可以传递给 System.out.println()(即不是 void)。它不会检查在这一点上该方法是否引用了尚未被明确赋值的变量。因此从编译器的角度来看,一切都没问题。

本质上,编译器只检查你在构造函数或初始化器的主体中,在被明确赋值(初始化)之前,是否引用了 this.x。在构造函数中调用的其他方法被视为一个黑箱。

而在运行时,x 在这一点确实有一个值,尽管它是final,且在构造函数中尚未被赋值:它是 0。因此,这也不会导致运行时错误或其他未定义行为(像在某些其他语言中那样)。

这也是为什么在非最终类中不建议在构造函数中调用非final(可覆盖)方法的主要原因。如果你在构造函数中调用了一个非最终方法,而子类覆盖了它,方法可能引用尚未初始化的字段(因为子类构造函数会在父类构造函数之后再进行赋值)。这会导致该方法看到/使用默认值,或者如果子类构造函数显式赋值字段,则会覆盖方法设置的值,导致更新丢失。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章