Это не скомпилируется (с ошибкой 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.