为什么我必须对前向引用使用“this”关键字?

2022-08-31 16:21:02

当我使用关键字访问类中的非静态变量时,Java不会给出任何错误。但是当我不使用它时,Java会给出一个错误。为什么我必须使用?thisthis

我知道我通常什么时候应该使用,但这个例子与正常用法非常不同。this

例:

class Foo {
//  int a = b; // gives error. why ?
    int a = this.b; // no error. why ?
    int b;
    int c = b;

    int var1 = this.var2; // very interesting
    int var2 = this.var1; // very interesting
}

答案 1

完整的描述在 Java 语言规范的第 8.3.3 节:“字段初始化期间的转发引用

前向引用(引用此时尚未声明的变量)只有在以下情况都为真时才是错误的:

  • 类或接口 C 中实例变量的声明在使用实例变量后以文本方式出现;

  • 该用法是 C 的实例变量初始值设定项或 C 的实例初始值设定项中的简单名称;

  • 使用不在作业的左侧;

  • C 是包含用法的最内层类或接口。

请参阅粗体文本:“使用是一个简单的名称”。简单名称是没有进一步限定的变量名称。在代码中,是一个简单的名称,但不是。bthis.b

但是为什么?

原因是,正如JLS示例中的草书文本所述:

“上述限制旨在在编译时捕获循环或其他格式错误的初始化。

换句话说,他们允许,因为他们认为一个合格的参考使你更有可能仔细考虑你正在做的事情,但简单地使用可能意味着你犯了一个错误。this.bb

这就是Java语言设计者的基本原理。据我所知,在实践中是否如此,从未被研究过。

初始化顺序

为了扩展上述内容,参考Duklein对这个问题的评论,使用合格的参考文献可能不会给你想要的结果。this.b

我将此讨论限制在实例变量上,因为OP仅引用了它们。实例变量的分配顺序在 JLS 12.5 创建新类实例中进行了描述。您需要考虑到首先调用超类构造函数,并且初始化代码(赋值和初始化块)按文本顺序执行。

所以给定

int a = this.b;
int b = 2;

你最终会得到零(执行 初始值设定项时的值)和 2。abab

如果超类构造函数调用在子类中被覆盖的方法,并且该方法将值赋给 , 则可以获得更奇怪的结果。b

因此,一般来说,最好相信编译器并重新排序字段,或者在循环初始化的情况下修复潜在问题。

如果你需要使用来解决编译器错误,那么你可能正在编写代码,而这些代码将很难由你之后的人维护。this.b


答案 2

首先声明变量,然后分配变量。该类与此相同:

class Foo {
    int a;
    int b;
    int c = b;

    int var1;
    int var2;

    public Foo() {
        a = b;

        var1 = var2;
        var2 = var1;
    }
}

你不能这样做的原因是,在创建对象时尚未定义,但对象本身(即)与其所有成员变量一起存在。int a = b;bthis

以下是每种方法的说明:

    int a = b; // Error: b has not been defined yet
    int a = this.b; // No error: 'this' has been defined ('this' is always defined in a class)
    int b; 
    int c = b;  // No error: b has been defined on the line before