60

当我使用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
}
4

5 回答 5

71

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

前向引用(指的是此时尚未声明的变量)仅在以下全部为真时才会出现错误:

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

  • use 是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时的值)并且为 2。ab

如果超类构造函数调用在子类中被覆盖的方法并且该方法为b.

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

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

于 2017-10-27T05:43:11.520 回答
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  
于 2017-10-27T05:35:40.180 回答
4

您提出了 3 个案例:

  1. int a = b; int b;
    这会产生错误,因为编译器会b在内存中查找并且它不会在那里。但是当您使用this关键字时,它明确指定b在类的范围内定义,所有类引用都将被查找它,最后它会找到它。
  2. 第二种情况非常简单,正如我所描述的,b在之前的范围中定义,在内存中 c查找时不会成为问题。b
  3. int var1 = this.var2;
    int var2 = this.var1;
    在这种情况下没有错误,因为在每种情况下,变量都是在类中定义的,并且赋值使用this它将在类中查找分配的变量,而不仅仅是它后面的上下文。
于 2017-10-27T06:16:02.263 回答
4

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

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 ?会产生编译时错误,因为在 Javab中声明之后a是一个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时,它将不存在,并且会查找先前声明的不存在的属性。

于 2017-10-27T06:22:04.123 回答
3

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

这就是原因,IMO:The usage is via a simple name.

因此,在这种情况下,您必须使用this.

于 2017-10-27T05:38:09.157 回答