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) выполняется перед добавлением. ОК, это не поле, но оно также не объявлено в данный момент!
Могу ли я это как-то понять? Или я просто запомню эту разницу?
Может быть, это тоже немного связано:
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, поэтому результат должен быть 1 + 1 + 1, а 3 должен быть (переназначен) на x в результате вычисления выражения Test.x + (x=1) + Test.x
.
ЧАСТИЧНЫЙ ОТВЕТ: На самом деле, результаты зависят от реализации. JLS гарантирует только порядок, в котором оцениваются операнды бинарного оператора (слева направо) . Но если у нас есть бинарных операторов (скажем, плюс) с одинаковым приоритетом, их порядок вычисления НЕ гарантируется .
В моем случае операторы «плюс» оцениваются в крайнем левом положении, поэтому static «int x = Test.x (ZERO) + (x = 1) + Test.x (IS 1 after (x = 1));» 0 + 1 + 1 (помните, x = 1 - оператор, который возвращает присвоенное значение).
Опять же в моем случае в методе "int y = y + (y = 1) + y;" Крайний левый оператор плюс вычисляется первым (с ошибкой), но , если 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;
P.S. Это, безусловно, не является дубликатом значений по умолчанию и инициализации в Java - здесь нет прямого объяснения. Здесь нам нужны не только значения по умолчанию (ноль для int), но и множество различных правил в очень сложной комбинации (приоритет оператора, и особенно некоторые редкие особенности назначения!). Также я знаю, что приоритет присваивания здесь самый низкий, что присваивание является оператором, и оно возвращает значение!