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!
}
}
In the first case I understand that: when assignment (or subsequent addition?) is performed, p is treated as not declared.
But why is it different within a method? OK t is not treated as uninitialized because (t=1) is performed before addition. OK, t is not a field, but it is also not declared at the moment!
Can I understand it somehow? Or I shall just memorize this difference?
Maybe this is also related a bit to the same:
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
}
Why 2? First (x=1) is somehow evaluated (x is not declared!!!), then it returns 1, now x is already assigned (!?) and contains 1, so both Test.x is 1, but (x=1) operator also returned 1 so result shall be 1 + 1 + 1 and 3 shall be (reassigned) into x as a result of evaluating Test.x + (x=1) + Test.x
expression.
PARTIAL ANSWER: Actually, the results are implementation specific. JLS guarantees only the order in which operands of a binary operator are evaluated (left-to-right). But if we have binary operators (say, plus) with same priority, their order of evaluation is NOT guaranteed. In my case plus operators are evaluated left-most first, this is why static "int x = Test.x (ZERO) + (x=1) + Test.x (IS 1 after (x=1));" is 0 + 1 + 1 (remember, x=1 is an operator that returns assigned value). Again in my case within method "int y = y + (y=1) + y;" leftmost plus operator is evaluated first (giving error), but if JVM chose to evaluate second plus operator first, then it is guaranteed to evaluate its left operand first and (y=1) would make the y variable initialized (so the code would compile!)
I am still not sure why (x=1) is not treated as undeclared with fields. I vaguely remember that JLS allows undeclared variable in LHS (so any assignment works), but not in RHS (x++, int sth=x). I can memorize it using the following snippet:
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. This is surely not a duplicate of Default Values and Initialization in Java - there is no direct explanation there. Here we need not only default values (zero for int) rules, but a lot of different rules in a very COMPLEX combination (operator precedence, and especially some rare peculiarities of assignment!). Also I know that assignment precedence is lowest here and that assignment is an operator and it returns value!