为什么必须使用 "this" 关键字进行前向引用?
Why must I use the "this" keyword for forward references?
当我使用 this
关键字访问 class 中的非静态变量时,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
}
变量是先声明后赋值的。那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
在创建对象时还没有定义,但是对象本身(即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
请查看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
指定名称。
完整的描述在section 8.3.3 of the Java Language Specification: "Forward References During Field Initialization"
前向引用(指的是此时尚未声明的变量)只有在满足以下所有条件时才会出错:
The declaration of an instance variable in a class or interface C appears textually after a use of the instance variable;
The use is a simple name in either an instance variable initializer of C or an instance initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
查看粗体文本:"the use is a simple name"。简单名称是没有进一步限定的变量名称。在您的代码中,b
是一个简单名称,但 this.b
不是。
但是为什么呢?
原因是,正如 JLS 示例中的草书文本所述:
"The restrictions above are designed to catch, at compile time,
circular or otherwise malformed initializations. "
换句话说,他们允许 this.b
,因为他们认为合格的参考资料更有可能让您仔细考虑自己在做什么,但简单地使用 b
可能意味着你弄错了。
这就是 Java 语言设计者的基本原理。据我所知,这在实践中是否属实从未被研究过。
初始化顺序
为了扩展上述内容,参考 Dukeling 对问题的评论,使用合格的参考 this.b
可能不会给您想要的结果。
我将讨论限制在实例变量上,因为 OP 只引用了它们。
JLS 12.5 Creation of New Class Instances 中描述了分配实例变量的顺序。
您需要考虑首先调用超类构造函数,并且初始化代码(赋值和初始化块)按文本顺序执行。
所以给出
int a = this.b;
int b = 2;
你最终会得到 a
为零(执行 a
的初始化程序时 b
的值)和 b
为 2。
如果超类构造函数调用一个在子类中被重写的方法并且该方法为 b
.
赋值,甚至可以实现更奇怪的结果
因此,一般来说,相信编译器并重新排序您的字段或解决循环初始化的潜在问题是个好主意。
如果您需要使用 this.b
来绕过编译器错误,那么您编写的代码可能很难被您之后的人维护。
您提交了3个案例:
int a = b;
int b;
这会产生错误,因为编译器会在内存中查找 b
而它不会在那里。但是当你使用 this
关键字时,它会明确指定 b
是在 class 的范围内定义的,所有 class 引用都将被查找,最后它会找到它。
- 第二种情况非常简单,正如我所描述的,
b
是在 c
之前的范围内定义的,在内存中查找 b
时不会有问题。
int var1 = this.var2;
int var2 = this.var1;
在这种情况下没有错误,因为在每种情况下变量都在 class 中定义并且赋值使用 this
它将在 class 中查找分配的变量,而不仅仅是它所遵循的上下文通过.
对于 Java 中的任何 class 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
仅表示当前 class 引用或访问方法、构造函数或属性的引用。
A a1 = new A(); // Here this is nothing but a1
a1.test(); // Here this is again a1
当我们说 a = this.b;
时,它指定 b
是当前的 class 属性,但是当我们说 a = b;
时,因为它不在非静态中块 this
将不存在并将查找先前声明的不存在的属性。
当我使用 this
关键字访问 class 中的非静态变量时,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
}
变量是先声明后赋值的。那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
在创建对象时还没有定义,但是对象本身(即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
请查看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
指定名称。
完整的描述在section 8.3.3 of the Java Language Specification: "Forward References During Field Initialization"
前向引用(指的是此时尚未声明的变量)只有在满足以下所有条件时才会出错:
The declaration of an instance variable in a class or interface C appears textually after a use of the instance variable;
The use is a simple name in either an instance variable initializer of C or an instance initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
查看粗体文本:"the use is a simple name"。简单名称是没有进一步限定的变量名称。在您的代码中,b
是一个简单名称,但 this.b
不是。
但是为什么呢?
原因是,正如 JLS 示例中的草书文本所述:
"The restrictions above are designed to catch, at compile time, circular or otherwise malformed initializations. "
换句话说,他们允许 this.b
,因为他们认为合格的参考资料更有可能让您仔细考虑自己在做什么,但简单地使用 b
可能意味着你弄错了。
这就是 Java 语言设计者的基本原理。据我所知,这在实践中是否属实从未被研究过。
初始化顺序
为了扩展上述内容,参考 Dukeling 对问题的评论,使用合格的参考 this.b
可能不会给您想要的结果。
我将讨论限制在实例变量上,因为 OP 只引用了它们。 JLS 12.5 Creation of New Class Instances 中描述了分配实例变量的顺序。 您需要考虑首先调用超类构造函数,并且初始化代码(赋值和初始化块)按文本顺序执行。
所以给出
int a = this.b;
int b = 2;
你最终会得到 a
为零(执行 a
的初始化程序时 b
的值)和 b
为 2。
如果超类构造函数调用一个在子类中被重写的方法并且该方法为 b
.
因此,一般来说,相信编译器并重新排序您的字段或解决循环初始化的潜在问题是个好主意。
如果您需要使用 this.b
来绕过编译器错误,那么您编写的代码可能很难被您之后的人维护。
您提交了3个案例:
int a = b; int b;
这会产生错误,因为编译器会在内存中查找b
而它不会在那里。但是当你使用this
关键字时,它会明确指定b
是在 class 的范围内定义的,所有 class 引用都将被查找,最后它会找到它。- 第二种情况非常简单,正如我所描述的,
b
是在c
之前的范围内定义的,在内存中查找b
时不会有问题。 int var1 = this.var2;
int var2 = this.var1;
在这种情况下没有错误,因为在每种情况下变量都在 class 中定义并且赋值使用this
它将在 class 中查找分配的变量,而不仅仅是它所遵循的上下文通过.
对于 Java 中的任何 class 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
仅表示当前 class 引用或访问方法、构造函数或属性的引用。
A a1 = new A(); // Here this is nothing but a1
a1.test(); // Here this is again a1
当我们说 a = this.b;
时,它指定 b
是当前的 class 属性,但是当我们说 a = b;
时,因为它不在非静态中块 this
将不存在并将查找先前声明的不存在的属性。