为什么我必须在转发引用中使用“this”关键字?

时间:2017-10-27 04:54:40

标签: 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
}

5 个答案:

答案 0 :(得分:71)

完整说明位于section 8.3.3 of the Java Language Specification: "Forward References During Field Initialization"

如果以下都是真的,那么前向引用(指的是那时尚未声明的变量)只是一个错误:

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

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

  •   
  • 使用不在作业的左侧;

  •   
  • C是封闭使用的最里面的类或接口。

  •   

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

但为什么?

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

  

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

换句话说,他们允许this.b,因为他们认为合格的参考文件使您更有可能仔细考虑过您正在做的事情,但仅仅使用b可能意味着您制作了一个错误。

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

初始化顺序

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

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

所以给出

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

最终a为零(b执行初始化程序时a的值)且b为2。

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

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

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

答案 1 :(得分:45)

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

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

    int var1;
    int var2;

    public Foo() {
        a = b;

        var1 = var2;
        var2 = var1;
    }
}

您无法执行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  

答案 2 :(得分:4)

您提出了3个案例:

  1. int a = b; int b;
    这会产生错误,因为编译器会在内存中查找b并且它不会存在。但是当你使用this关键字时,它明确指定b是在类的范围内定义的,所有类引用都将被查找,最后它会找到它。
  2. 第二种情况非常简单,正如我所描述的,bc之前的范围内定义,并且在内存中查找b时不会出现问题。
  3. int var1 = this.var2;
    int var2 = this.var1;
    在这种情况下没有错误,因为在每种情况下变量都在类中定义,赋值使用this,它将在类中查找赋值的变量,而不仅仅是它所遵循的上下文。

答案 3 :(得分:4)

对于Java中的任何类this是一个默认的引用变量(当没有给出特定的引用时),用户可以给出或者编译器将在非静态块内提供。例如

public class ThisKeywordForwardReference {

    public ThisKeywordForwardReference() {
        super();
        System.out.println(b);
    }

    int a;
    int b;

    public ThisKeywordForwardReference(int a, int b) {
        super();
        this.a = a;
        this.b = b;
    }

}

你说int a = b; // gives error. why ?给出了编译时错误,因为b是在a之后声明的,它是Java中的Illegal Forward Reference并被视为编译时错误。

但是,methods Forward Reference成为合法

int a = test();
int b;

int test() {
    return 0;
}

但在我的代码中,带有参数的构造函数在a和&之前声明。 b,但没有给出任何编译时错误,因为编译器会将System.out.println(b);替换为System.out.println(this.b);

关键字this仅表示当前类引用或访问方法,构造函数或属性的引用。

A a1 = new A();  // Here this is nothing but a1
a1.test();  // Here this is again a1

当我们说a = this.b;时,它指定b是当前的类属性,但当我们说a = b;时,因为它不在非静态块this中将不会出现,并将查找先前声明的不存在的属性。

答案 4 :(得分:3)

请查看Java语言规范:https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.2.3

这就是IMO:docker-compose start

的原因

因此,在这种情况下,您必须使用The usage is via a simple name.指定名称。