class Test{
int p = (p=1) + p; // ERR "Cannot reference a field before it is defined"
int q = (q=1) + this.q; //fine!
void f() {
int t = (t=1) + t; // fine!
}
}
Run Code Online (Sandbox Code Playgroud)
在第一种情况下,我了解:执行赋值(或后续加法?)时,p被视为未声明。
但是,为什么一个方法内有不同呢?OK t不会被视为未初始化,因为(t = 1)在加法之前执行。好的,t不是一个字段,但是目前还没有声明!
我能以某种方式理解吗?还是我会记住这种差异?
也许这也与以下内容有关:
static int x = Test.x + (x=1) + Test.x; // produces 2
void f() {
int y = y + (y=1) + y; // ERR local variable y may not have been initialized
}
Run Code Online (Sandbox Code Playgroud)
为什么是2?首先以某种方式评估(x = 1)(未声明x!),然后返回1,现在x已分配(!?)并包含1,因此两个Test.x均为1,但是(x = 1 )运算子也传回1,因此运算结果必须是1 +1 + 1,而3必须(赋值)到x中作为Test.x + (x=1) + Test.x运算式。
部分答案:实际上,结果是特定于实现的。JLS仅保证对二进制运算符的操作数求值的顺序(从左到右)。但是,如果我们拥有具有相同优先级的二元运算符(例如加号),则不能保证它们的求值顺序。在我的情况下,加上运算符首先在最左边计算,这就是为什么静态“ int x = Test.x(零)+(x = 1)+ Test.x(在(x = 1)之后为IS 1);” 是0 + 1 + 1(请记住,x = 1是返回分配值的运算符)。同样在我的情况下,方法是“ int y = y +(y = 1)+ y;” 最左边的plus运算符首先被评估(给出错误),但是如果JVM选择首先评估第二个加号运算符,那么可以保证首先评估其左操作数,并且(y = 1)将初始化y变量(因此代码可以编译!)
我仍然不确定为什么(x = 1)不会被视为未声明的字段。我模糊地记得,JLS在LHS中允许未声明的变量(因此任何赋值都可行),但在RHS中则不允许(x ++,int sth = x)。我可以使用以下代码段来记住它:
class Test {
{ x = 7; } // fine! Initializer is like a regular method
int x;
static { y = 7; } // fine! Initializer is like a regular method
static int y;
Run Code Online (Sandbox Code Playgroud)
PS这肯定不是Java中默认值和初始化的重复-那里没有直接的解释。在这里,我们不仅需要默认值(对于int为零)规则,而且还需要非常复杂的组合中的许多不同规则(运算符优先级,尤其是赋值的某些罕见特性!)。我也知道分配优先级最低,分配是一个运算符,它返回值!
| 归档时间: |
|
| 查看次数: |
188 次 |
| 最近记录: |