Почему переменная final не всегда является константным выражением?

В приведенном ниже коде:

final int a;
a=2;
byte b=a;   // error: possible loss of precision

Почему я получаю эту ошибку? Разве a final переменная не является постоянным выражением времени компиляции и, следовательно, неявно сужается до байта во время присваивания?

Другими словами, приведенный выше код не эквивалентен:

final int a=2;
byte b=a;

person paidedly    schedule 11.06.2015    source источник


Ответы (3)


Компилятор не такой умный.

Мы можем сказать, что значение всегда будет равно 2. Но что, если бы у нас было что-то подобное?

class ABC{
    final int a;

    public ABC(){
       if(Math.random() < .5){
          a = 2;
       }
       else{
          a = 12345;
       }

       byte b = a;
    }
}

Компилятор недостаточно умен, чтобы отличить эти два случая друг от друга, поэтому вместо этого он выдает ошибку.

person Kevin Workman    schedule 11.06.2015
comment
На самом деле компилятору не позволено быть таким умным. - person usr; 12.06.2015
comment
@usr Это какая-то серьезная примитивная религия. Серьезно: Почему это? Компиляторы чувствительны к числам 1, 2, 4 и т. д. при умножении. Случай в этом ответе явно неразрешим, потому что значения, возвращаемые из random, зависят от времени запуска программы. - person LyingOnTheSky; 12.06.2015
comment
@LyingOnTheSky Но даже если бы вычисление было детерминированным и компилятор мог бы его понять, ему все равно не разрешалось бы рассматривать его как постоянное выражение (удаляя необходимость в приведении). Важно, чтобы то, что является или не является действительной Java-программой, не зависело от того, насколько умен конкретный компилятор. - person CodesInChaos; 12.06.2015
comment
@CodesInChaos Вы просто говорите, что компиляторы должны придерживаться стандарта. Хорошая точка зрения. - person LyingOnTheSky; 12.06.2015
comment
@LyingOnTheSky В значительной степени. В C++ компиляторы обычно добавляют свои собственные расширения. В Java это в принципе запрещено (см. иск Microsoft). - person user253751; 12.06.2015

Из JLS

Пустой final – это переменная final, в объявлении которой отсутствует инициализатор.

Константная переменная — это переменная final примитивного типа или типа String, которая инициализируется константным выражением (§15.28).

Ваша переменная

final int a;

является пустой final переменной. Не хватает инициализатора. Второй абзац к нему не относится, потому что он не инициализируется при объявлении. Следовательно, это не постоянное выражение.

Это касается и полей.

person Sotirios Delimanolis    schedule 11.06.2015

Поскольку переменные final могут быть отложены при инициализации, и компилятор не может определить для b, что оно имеет значение в ветке case.

person John Stadermann    schedule 11.06.2015