Использование this.var во время инициализации var

Изучая другой вопрос, я с удивлением обнаружил, что следующий Java код компилируется без ошибок:

public class Clazz {
    int var = this.var + 1;
}

В моем JDK6 var инициализируется 1.

Имеет ли приведенный выше код четко определенную семантику или его поведение не определено? Если вы говорите, что это четко определено, укажите соответствующие части JLS.


person NPE    schedule 05.04.2013    source источник
comment
@Sudhanshu: var используется для инициализации var.   -  person NPE    schedule 05.04.2013
comment
Сначала this.var = 0, потом прибавляешь на 1, тогда станет 1   -  person Iswanto San    schedule 05.04.2013
comment
@IswantoSan: Это вполне может иметь место, но, пожалуйста, не могли бы вы подтвердить это утверждение, процитировав JLS.   -  person NPE    schedule 05.04.2013
comment
@NPE Думаю, это это ссылка, которую вы хотите; целые числа по умолчанию равны нулю.   -  person adrianp    schedule 05.04.2013
comment
@adrianp, тогда почему int var = var+1; не может успешно скомпилироваться?   -  person AmitG    schedule 05.04.2013
comment
@AmitG Локальной переменной (§14.4, §14.14) должно быть явно присвоено значение перед ее использованием либо путем инициализации (§14.4), либо присваивания (§15.26) таким образом, чтобы компилятор мог проверить это с помощью правил для определенного назначения (§ 16).   -  person adrianp    schedule 05.04.2013
comment
@adrianp var в этом примере не является локальной переменной.   -  person assylias    schedule 05.04.2013
comment
@AmitG Вы должны были быть более откровенными. Это ограничение указано здесь если я не ошибаюсь.   -  person adrianp    schedule 05.04.2013


Ответы (6)


Он мимоходом упоминается в примере 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);
    }
}

надпись гласит:

... инициализатор переменной для i использует метод класса peek для доступа к значению переменной j до того, как j будет инициализирован своим инициализатором переменной, после чего он все еще имеет значение по умолчанию (§4.12. 5).

Это должно напрямую соответствовать вашей ситуации.

person Keppil    schedule 05.04.2013
comment
Блин, вот оно, спасибо. - person NPE; 05.04.2013

Глава 8.3.2.2. пункт 2:

Выражения инициализации для переменных экземпляра могут ссылаться на текущий объект this (§15.8.3) и использовать ключевое слово super (§15.11.2, §15.12).

Хотя в следующем абзаце добавляется:

Использование переменных экземпляра, объявления которых появляются в текстовом виде после использования, иногда ограничено, даже если эти переменные экземпляра находятся в области видимости. См. §8.3.2.3 для точных правил, регулирующих прямую ссылку на переменные экземпляра.

person Matej 'Yin' Gagyi    schedule 05.04.2013

Введение в главу 16

В главе 16 описывается точный способ, которым язык гарантирует, что локальные переменные точно установлены перед использованием. В то время как все другие переменные автоматически инициализируются значением по умолчанию, язык программирования Java не инициализирует автоматически локальные переменные, чтобы избежать маскирования ошибок программирования.

person Achintya Jha    schedule 05.04.2013

Использование простого имени не допускается в случае 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;
}
person AmitG    schedule 05.04.2013
comment
Это было определено в вопросе, указанном в верхней части этого вопроса. NPE спрашивает: есть ли у нас гарантия, что var будет единицей после int var = this.var + 1; и почему? - person assylias; 05.04.2013

Поскольку всем нелокальным переменным присваивается начальное значение (а присваивание происходит — раньше всего остального), нет проблем прочитать их в любой момент.

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

На самом деле программист может легко обойти ограничения и в любом случае «косвенно» получить доступ к полю; семантика этого доступа такая же, как и у «прямого» доступа, если бы он был разрешен.

int var = this.var + 1;       // suppose javac forbids this

int var = this.getVar() + 1;  // but can javac forbid this?
person ZhongYu    schedule 05.04.2013

В этом нет ничего плохого. это ключевое слово относится к текущему объекту и используется для различения локальной переменной и переменной экземпляра. Точно так же значение локальной переменной может быть присвоено переменной экземпляра, возможно и наоборот. Это означает, что мы можем присвоить значение переменной экземпляра локальной переменной.

обратитесь к главе 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; 
      } 
    }
person Julie Edward    schedule 05.04.2013
comment
ОП вопрос другой. Вопрос касается forward references. Вопрос OP не говорит о локальной переменной и переменной экземпляра. - person AmitG; 05.04.2013