为什么我必须使用"this"关键字进行前向引用?

Nom*_*mad 59 java class this forward-reference

当我使用this关键字访问类中的非静态变量时,Java不会给出任何错误.但是当我不使用它时,Java会出错.我为什么要用this

我知道通常应该什么时候使用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
}
Run Code Online (Sandbox Code Playgroud)

Erw*_*idt 71

完整描述见Java语言规范的8.3.3节:" 字段初始化期间的前向引用 "

如果以下全部为真,则前向引用(指的是此时尚未声明的变量)仅为错误:

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

  • 在C的实例变量初始值设定项或C的实例初始值设定项中使用是一个简单的名称 ;

  • 使用不在作业的左侧;

  • C是封闭使用的最内层类或接口.

请参阅粗体文字:"使用是一个简单的名称".简单名称是变量名称,无需进一步限定.在您的代码中,b是一个简单的名称,但this.b不是.

但为什么?

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

"上述限制旨在在编译时捕获循环或其他错误的初始化."

换句话说,他们允许,this.b因为他们认为合格的参考使你更有可能仔细考虑你正在做什么,但仅仅使用b可能意味着你犯了一个错误.

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

初始化顺序

为了扩展上述内容,参考Dukeling对该问题的评论,使用合格的参考资料this.b可能无法为您提供所需的结果.

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

所以给定

int a = this.b;
int b = 2;
Run Code Online (Sandbox Code Playgroud)

你最终会a得到零(执行初始化程序b时的值a)并且b为2.

如果超类构造函数调用在子类中重写的方法并且该方法为其赋值,则甚至可以实现更奇怪的结果b.

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

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


Jas*_*son 45

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

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

    int var1;
    int var2;

    public Foo() {
        a = b;

        var1 = var2;
        var2 = var1;
    }
}
Run Code Online (Sandbox Code Playgroud)

你不能做的原因int a = b;是因为b在创建对象时尚未定义,但是对象本身(即this)存在于其所有成员变量中.

以下是每个的说明:

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

  • 这个答案只是重申了问题的条款.它没有以任何有意义的方式解释为什么. (9认同)
  • "`b`在创建对象时尚未定义" - 字段在编译时定义,对象在运行时创建,因此这可能不正确.出于这个原因,我倾向于这个答案,并赞成了Erwin的. (2认同)