Почему int p = (p = 1) + p;Ошибка в определениях полей, но это нормально в методе? - PullRequest
8 голосов
/ 11 июля 2019
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), но и множество различных правил в очень сложной комбинации (приоритет оператора, и особенно некоторые редкие особенности назначения!). Также я знаю, что приоритет присваивания здесь самый низкий, что присваивание является оператором, и оно возвращает значение!

Ответы [ 2 ]

1 голос
/ 11 июля 2019

Прочитать описание локальных переменных в Спецификация языка Java .Ваша точная проблема описана в Примере 6.3-2.Описание таково:

Следующая программа вызывает ошибку во время компиляции, потому что инициализация локальной переменной p находится в рамках объявления локальной переменной p, но локальная переменная p пока не имеетимеют значение и не могут быть использованы.

0 голосов
/ 12 июля 2019

Может быть, я не буду очень подробен, но я попробую, вы указали на очень хорошие примеры жизненного цикла переменной в 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 inline.

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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...