Подтвердить что ты не робот

Почему Java требуется явное приведение к конечной переменной, если она была скопирована из массива?

Начиная со следующего кода...

byte foo = 1;
byte fooFoo = foo + foo;

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

Ошибка: (5, 27) java: несовместимые типы: возможное преобразование с потерями из int в байт

... но если foo является окончательным...

final byte foo = 1;
final byte fooFoo = foo + foo;

файл будет скомпилирован успешно.

Переход к следующему коду...

final byte[] fooArray = new byte[1];
fooArray[0] = 1;

final byte foo = fooArray[0];
fooArray[0] = 127;

System.out.println("foo is: " + foo);

... напечатает

foo is: 1

... это нормально. Значение копируется в конечную переменную и больше не может быть изменено. Воспроизведение со значением в массиве не изменяет значение foo (как и ожидалось...).

Для чего требуется следующее литье:

final byte[] fooArray = new byte[1];
fooArray[0] = 1;
final byte foo = fooArray[0];
final byte fooFoo = foo + foo;

Как это отличается от второго примера в этом вопросе? Почему компилятор дает мне следующую ошибку?

Ошибка: (5, 27) java: несовместимые типы: возможное преобразование с потерями из int в байт

Как это может случиться?

4b9b3361

Ответ 1

JLS (§5.2) имеет специальные правила для преобразования присваивания с постоянными выражениями:

Кроме того, если выражение является константным выражением (§15.28) типа byte, short, char, или int:

  • Сужение примитивного преобразования может быть использовано, если тип переменной byte, short или char, а значение константного выражения представляется в типе переменной.

Если мы перейдем к ссылке выше, мы увидим их в определении постоянного выражения:

  • Литералы примитивного типа и литералов типа String
  • Аддитивные операторы + и -
  • Простые имена (§6.5.6.1), которые относятся к постоянным переменным (§4.12.4).

Если мы следуем второй ссылке выше, мы видим, что

Переменная примитивного типа или типа String, то есть final и инициализируется константой постоянной времени компиляции (§15.28), называется постоянной переменной.

Отсюда следует, что foo + foo может быть назначено только fooFoo, если foo - постоянная переменная. Чтобы применить это к вашим случаям:

  • byte foo = 1; не определяет постоянную переменную, так как она не final.

  • final byte foo = 1; делает определение постоянной переменной, потому что она final и инициализируется константным выражением (примитивный литерал).

  • final byte foo = fooArray[0]; не определяет константную переменную, потому что она не инициализируется константным выражением.

Обратите внимание, что сам fooFoo final не имеет значения.

Ответ 2

Значение 1 отлично вписывается в байты; так 1 + 1; и когда переменная является окончательной, компилятор может выполнять постоянную фальцовку. (другими словами: компилятор не использует foo при выполнении этой операции +, но значения "raw" 1)

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

Для второй части: создание окончательного массива по-прежнему позволяет изменять любое из его полей; так снова; нет возможности постоянного складывания; так что операция "расширения" снова начинается.

Ответ 3

Это действительно то, что компилятор делает при постоянной сгибании при использовании с final, как мы видим из байтового кода:

    byte f = 1;
    // because compiler still use variable 'f', so `f + f` will 
    // be promoted to int, so we need cast
    byte ff = (byte) (f + f);
    final byte s = 3;
    // here compiler will directly compute the result and it know
    // 3 + 3 = 6 is a byte, so no need cast
    byte ss = s + s;
    //----------------------
    L0
    LINENUMBER 12 L0
    ICONST_1 // set variable to 1
    ISTORE 1 // store variable 'f'
    L1
    LINENUMBER 13 L1
    ILOAD 1 // use variable 'f'
    ILOAD 1
    IADD
    I2B        
    ISTORE 2 // store 'ff'
    L2

    LINENUMBER 14 L2
    ICONST_3 // set variable to 3
    ISTORE 3 // store 's'
    L3
    LINENUMBER 15 L3
    BIPUSH 6 // compiler just compute the result '6' and set directly
    ISTORE 4 // store 'ss'

И если вы измените свой последний байт на 127, он также будет жаловаться:

    final byte s = 127;
    byte ss = s + s;

в этом случае компилятор вычислит результат и узнает об этом из-за пределов, поэтому он все равно будет жаловаться, что они несовместимы.

Подробнее:

И здесь - еще один вопрос о постоянной сгибании строки: