为什么我必须使用“this”关键字作为前向引用?
当我使用this
关键字来访问一个类中的非静态variables时,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 }
variables先声明然后赋值。 那个class级和这个一样:
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
在创build对象时尚未定义,但是对象本身(即this
)与其所有成员variables一起存在。
以下是对每一个的描述:
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
Java语言规范第8.3.3节“ 字段初始化期间的前向引用 ”
前向引用(指的是那个时候还没有声明的variables)只有在以下情况下才是错误的:
在实例variables的使用之后,类或接口C中的实例variables的声明以文本forms出现;
在C的实例variables初始值设定项或C的实例初始值设定项中,使用是简单的名称 ;
使用不在作业的左侧;
C是封闭使用的最内层的类或接口。
看到粗体文本:“使用是一个简单的名字”。 简单的名字是一个没有进一步资格的variables名称。 在你的代码中, b
是一个简单的名字,但是this.b
不是。
但为什么?
原因是,正如JLS例子中的草书所述:
“上面的限制旨在在编译时捕获循环或其他格式不正确的初始化。”
换句话说,他们允许this.b
是因为他们认为合格的参考使得你更仔细地思考过你在做什么,而仅仅使用b
可能意味着你犯了一个错误。
这是Java语言的devise者的基本原理。 据我所知,在实践中这是否属实,从未被研究过。
初始化顺序
为了扩大上述内容,参考杜克林对这个问题的评论,使用一个合格的参考文献this.b
将可能不会给你你想要的结果。
我限制这个讨论的实例variables,因为OP只提到他们。 JLS 12.5创build新类实例中描述了实例variables分配的顺序。 您需要考虑到首先调用超类构造函数,并且初始化代码(分配和初始化块)按文本顺序执行。
如此给予
int a = this.b; int b = 2;
你将以0为零(执行初始化程序时的b
值)和b
为2。
如果超类的构造函数调用在子类中重写的方法,并且该方法为b
赋值,则甚至可以实现更奇怪的结果。
所以,一般来说,相信编译器是一个好主意,要么重新sorting你的字段,要么在循环初始化的时候解决底层的问题。
如果您需要使用this.b
来解决编译器错误,那么您可能编写的代码将很难由后面的人维护。
你已经提出了三种情况:
-
int a = b; int b;
这会产生错误,因为编译器会在内存中查找b
,并且它不会在那里。 但是当你使用this
关键字时,它明确地指定了b
在类的范围内被定义,所有的类的引用将被查找,最后它会find它。 - 第二种情况非常简单,正如我所描述的,
b
是在c
之前的范围内定义的,并且在内存中查找b
时不会成为问题。 -
int var1 = this.var2;
int var2 = this.var1;
在这种情况下,没有错误,因为在每种情况下,variables都是在类中定义的,赋值使用它将在类中寻找指定的variables,而不仅仅是后面的上下文。
对于Java中的任何类, this
是一个默认的引用variables(当没有给出具体的引用时),用户可以给出或编译器将在非静态块内提供。 例如
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 ?
int a = b; // gives error. why ?
给出了编译时错误,因为b
是在Java中Illegal Forward Reference
之后声明a
,并被认为是编译时错误。
但在methods
的情况下, Forward Reference
变得合法
int a = test(); int b; int test() { return 0; }
但是在我的代码中,带有参数的构造函数在a
& b
之前声明,但是没有给出任何编译时错误,因为System.out.println(b);
将被System.out.println(this.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
不会出现,并且会查找先前声明的不存在的属性。
请查看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
指定名称。