Использование this.var во время инициализации var
При исследовании еще одного вопроса я с удивлением обнаружил, что следующий код Java компилируется без ошибок:
public class Clazz {
int var = this.var + 1;
}
В моем JDK6 var
инициализируется до 1
.
Является ли приведенный выше код хорошо определенной семантикой или его поведение undefined? Если вы говорите это четко, процитируйте соответствующие части JLS.
Ответы
Ответ 1
Об этом говорится в примере 8.3.2.3-1 в разделе раздел 8.3.2.3. В тексте к примеру
class Z {
static int peek() { return j; }
static int i = peek();
static int j = 1;
}
class Test {
public static void main(String[] args) {
System.out.println(Z.i);
}
}
заголовок говорит:
... инициализатор переменной для я использует метод peek метода для доступа к значению переменной j до того, как j был инициализирован его инициализатором переменной, после чего он по-прежнему имеет значение по умолчанию (§4.12. 5).
Это должно отображаться непосредственно в вашей ситуации.
Ответ 2
Глава 8.3.2.2. пункт 2:
В выражениях инициализации для переменных экземпляра разрешено ссылаться на текущий объект (§15.8.3) и использовать ключевое слово super (§15.11.2, §15.12).
В следующем абзаце добавляется:
Использование переменных экземпляра, объявления которых появляются по тексту после использования, иногда ограничены, хотя эти переменные экземпляра находятся в области видимости. См. Раздел 8.3.3.3 для точных правил, регулирующих прямую ссылку на переменные экземпляра.
Ответ 3
Введение главы 16
В главе 16 описывается точный способ обеспечения языка что локальные переменные определенно установлены перед использованием. Хотя все остальные переменные автоматически инициализируются значением по умолчанию, Java язык программирования не автоматически инициализирует локальные переменные чтобы избежать маскировки ошибок программирования.
Ответ 4
Использование простого имени не допускается в случае forward references
согласно JSL.
Поэтому для доступа к таким переменным необходимо использовать this.
class UseBeforeDeclaration {
int h = j++; // error - `j` read before declaration
int l = this.j * 3; // ok - not accessed via simple name
int j;
}
Ответ 5
Так как всем нелокальным переменным присваивается начальное значение (а назначение выполняется - перед всем остальным), нет никаких проблем с их чтением в любое время.
Спектр выборочно запрещает некоторые обращения в некоторых ситуациях, полагая, что такие обращения, скорее всего, являются ошибками программирования. Но если бы они были разрешены, у них была бы четко определенная семантика.
На самом деле программист может легко обойти ограничения и "косвенно" получить доступ к полю в любом случае; семантика этого доступа совпадает с "прямым" доступом, если это разрешено.
int var = this.var + 1; // suppose javac forbids this
int var = this.getVar() + 1; // but can javac forbid this?
Ответ 6
В этом нет ничего плохого. это ключевое слово относится к текущему объекту и используется для различения локальной переменной и переменной экземпляра. Значение локальной переменной также может быть присвоено переменной экземпляра, и наоборот. Это означает, что мы можем присвоить значение переменной экземпляра локальной переменной.
обратитесь к главе 4.12.3 Виды переменных из http://docs.oracle.com/javase/specs/jls/se7/jls7.pdf (стр. 80). Пример также приведен здесь.
**Example 4.12.3-1. Different Kinds of Variables**
class Point {
static int numPoints; // numPoints is a class variable
int x, y; // x and y are instance variables
int[] w = new int[10]; // w[0] is an array component
int setX(int x) { // x is a method parameter
int oldx = this.x; // oldx is a local variable
this.x = x;
return oldx;
}
}