Почему 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 в байт
Как это может случиться?
Ответы
Ответ 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;
в этом случае компилятор вычислит результат и узнает об этом из-за пределов, поэтому он все равно будет жаловаться, что они несовместимы.
Подробнее:
И здесь - еще один вопрос о постоянной сгибании строки: