Почему компилятор Java не понимает, что эта переменная всегда инициализируется?

class Foo{
    public static void main(String args[]){
        final int x=101;

        int y;
        if(x>100){
            y=-1;
        }
        System.out.println(y);
    }
}

Компилятор Java понимает условие оператора if всегда true и поэтому y всегда будет инициализирован. Нет ошибки компиляции, как и ожидалось.

class Bar{
    public static void main(String args[]){
        final int x;
        x=101;

        int y;      
        if(x>100){
            y=-1;
        }
        System.out.println(y);
    }
}

Но когда я разбиваю объявление и инициализацию x на две строки, компилятор, похоже, не понимает, что условие всегда выполняется и y всегда будет инициализироваться.

final int x;
x=101;
byte b;
b=x;
System.out.println(b);

Здесь происходит то же самое, и компилятор выдает ошибку потери точности.

final int x=101;
byte b;
b=x;
System.out.println(b);

Опять же, компилятор может понять, что x находится внутри диапазона b.

3 ответа

Решение

Это связано с тем, как компилятор определяет, будет ли выполняться оператор или нет. Это определено в JLS # 16:

Каждая локальная переменная и каждое пустое конечное поле должны иметь определенно назначенное значение при любом доступе к их значению.

В вашем случае компилятор не может определить, что y был определенно назначен и выдает ошибку. Это связано с тем, что необходимо определить, что условие всегда выполняется, и это возможно только в том случае, если условие в if это постоянное выражение.

JLS # 15.28 определяет константные выражения:

Выражение константы во время компиляции - это выражение, обозначающее значение типа примитива или String, которое не завершается внезапно и составляется с использованием только следующего:

  • [...]
  • Простые имена (§6.5.6.1), которые ссылаются на постоянные переменные (§4.12.4).

JLS #4.12.4 определяет постоянные переменные как:

Переменная примитивного типа или типа String, которая является окончательной и инициализируется константным выражением во время компиляции, называется константной переменной.

В твоем случае, final int x = 101; постоянная переменная, но final int x; x = 101; не является.

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

См. Спецификацию языка Java Глава 16. Определенное назначение

Критическое правило - это правило в 16.2.7. Заявления if, "if (e) S" case. Правило того, чтобы быть определенно назначенным, расширяется до:

V назначается после if (e) S в том и только в том случае, если V назначается после S, а V назначается после e при значении false.

у соответствующий V. Он не назначается перед оператором if. Он действительно назначается после S, y = {y = -1;}, но ничто не делает его назначенным, когда x>100 ложно.

Таким образом, y не определенно присваивается после оператора if.

Более полный анализ потока определил бы, что условие x>100 всегда выполняется, но JLS требует, чтобы компилятор отклонил программу на основе этих конкретных правил.

Последняя переменная в порядке. Правило на самом деле:

"Это ошибка времени компиляции, если конечная переменная назначена, если только она не была определенно не назначена (§16) непосредственно перед назначением".

Декларация оставляет его определенно неназначенным, и даже ограниченный анализ потока может определить, что x все еще определенно не назначен в присваивании.

Что вы сделали для переменной x во втором коде называется пустая конечная переменная. Если конечная переменная не инициализируется при объявлении, то она называется пустой конечной переменной.

Многие Java-разработчики считают, что значение конечной переменной известно во время компиляции. Это не всегда верно. Говорят, что значение пустой конечной переменной НЕ известно во время компиляции. Следовательно, ваш второй код выдаст вам ошибку компиляции. Компилятор может видеть, что вы инициализировали конечную переменную x, но компиляция не знает, что это такое. Таким образом, компилятор не может разрешить оператор if. Поэтому он считает, что переменная y не инициализируется.

Вы можете прочитать больше о финальных переменных Java здесь.

Другие вопросы по тегам