Это не скомпилируется (с ошибкой illegal forward reference
), как и следовало ожидать:
class test {
int x = x + 42;
}
Но это работает:
class test {
int x = this.x + 42;
}
Что происходит? Что назначается в последнем случае?
Это не скомпилируется (с ошибкой illegal forward reference
), как и следовало ожидать:
class test {
int x = x + 42;
}
Но это работает:
class test {
int x = this.x + 42;
}
Что происходит? Что назначается в последнем случае?
Сводка: оба инициализатора получают доступ к полю, которое еще не инициализировано (и, следовательно, по-прежнему имеет значение по умолчанию, равное нулю). Поскольку это, скорее всего, ошибка программирования, язык запрещает некоторые простые формы такого доступа. Однако это не запрещает более сложную форму.
Поведение совместимо с JLS, в частности §8.3.2.3. Ограничения на использование полей во время инициализации
Объявление элемента должно отображаться текстовым образом до его использования, только если элемент является экземпляром (соответственно
static
) поля класса или интерфейсаC
и выполняются все следующие условия:
Использование происходит в инициализаторе инициализатора экземпляра экземпляра (соответственно
static
) C или в экземпляре (соответственноstatic
).Использование не находится в левой части задания.
Использование осуществляется с помощью простого имени.
C
- самый внутренний класс или интерфейс, охватывающий использование.
Первый пример удовлетворяет всем четырем условиям и поэтому недействителен. Второй пример не удовлетворяет третьему условию (this.x
- не простое имя), и поэтому он ОК.
Общая последовательность событий следующая:
Таким образом, если инициализатор ссылается на поле, которое появляется позже в определении класса (или в самом поле), оно увидит значение по умолчанию этого другого поля. Вероятно, это будет ошибка программирования и поэтому явно запрещена в §8.3.2.3.
Если вы обходите §8.3.2.3, например, используя this.
для пересылки в поле, вы увидите значение по умолчанию (ноль для int
). Таким образом, четко определено и гарантировано установить x
на 42
:
class test {
int x = this.x + 42;
}
Слишком сложно обнаружить и запретить все обращения к x
во время инициализации x. Например
int x = that().x; | int x = getX();
|
Test that(){ return this; } | int getX(){ return x; }
Спектр останавливается при "доступе простым именем" и не пытается быть более полным.
В другом разделе "Определенное присвоение" спецификация делает аналогичную вещь. Например
public class Test
{
static final int y;
static final int z = y; // fail, y is not definitely assigned
static{ y = 1; }
}
public class Test
{
static final int y;
static final int z = Test.y; // pass... because it not a simple name
static{ y = 1; }
}
Интересно, что "Определенное присвоение" конкретно упоминает, что this.x
эквивалентно x
(или для поля, простое имя поля, присвоенного этим)
этот раздел может быть добавлен в раздел, цитируемый NPE.
- использование осуществляется через простое имя (или простое имя, присвоенное этим)
Но в конце во время компиляции невозможно проанализировать все возможные способы использования/доступа к полю.
В первом случае компилятор пытается оценить выражение "x + 42", но не получается, потому что x не инициализируется.
Во втором случае выражение 'this.x + 42' оценивается во время выполнения (из-за 'this' keyword), когда x уже инициализирован и имеет значение 0.