为什么int p =(p = 1)+ p;字段定义中失败,但是方法内可以吗?

时间:2019-07-11 17:09:56

标签: java

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!
    }       
}

在第一种情况下,我了解:执行赋值(或后续添加?)时,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
   }

为什么2?首先以某种方式评估(x = 1)(未声明x!),然后返回1,现在x已分配(!?)并包含1,因此两个Test.x均为1,但是(x = 1 )运算符还返回了1,因此求值Test.x + (x=1) + Test.x表达式的结果是x应当为1 +1 + 1,并且3应该(重新分配)为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选择首先评估第二个plus运算符,那么可以保证首先评估其左操作数,并且(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;

P.S。这肯定不是Default Values and Initialization in Java的重复-那里没有直接的解释。在这里,我们不仅需要默认值(对于int为零)规则,而且还需要非常复杂的组合中的许多不同规则(运算符优先级,尤其是赋值的某些罕见特性!)。我也知道分配优先级最低,分配是运算符,它返回值!

2 个答案:

答案 0 :(得分:1)

阅读Java Language Specification中局部变量声明的范围。例6.3-2中描述了您的确切问题。描述是这样的:

以下程序会导致编译时错误,因为局部变量p的初始化在局部变量p声明的范围之内,但是局部变量p尚无值,无法使用。

答案 1 :(得分:0)

也许我不会非常详细,但是我会尝试一下,您指出了Java中可变生命周期的非常好的例子。

int p = (p=1) + p;   // ERR "Cannot reference a field before it is defined"

在这种情况下,p是一个类字段,当编译器加载一个类p时尚未初始化(第一次扫描类,因此p尚未加载到内存中并且无法求值)。

void f() {
        int t = (t=1) + t; // fine!
}

在这种情况下,无论内部是什么,编译器都只会加载函数的定义(我的意思是如果没有语法错误,并且没有每个IDE都可以检查的错误类型)。这可能是一个奇怪的声明,但没关系,直到调用该函数并初始化t内联后才对其求值。

static int x = Test.x + (x=1) + Test.x; // produces 2

在这种情况下,x是一个静态变量,在类之前加载了静态的“东西”,因此您可以想象编译器将x字段放在您编写的所有内容之上。在这一行中,您说x等于1,所以1 +1 =2。这就像在做类似的事情

static int x = 1;
x = Test.x + Test.x;