En commençant par le code suivant ...
byte foo = 1;
byte fooFoo = foo + foo;
Lorsque j'essaie de compiler ce code, j'obtiens l'erreur suivante ...
Erreur: (5, 27) Java: types incompatibles: possible conversion avec perte de l'int en octet
... mais si foo
est final ...
final byte foo = 1;
final byte fooFoo = foo + foo;
le fichier sera compilé avec succès.
Passons au code suivant ...
final byte[] fooArray = new byte[1];
fooArray[0] = 1;
final byte foo = fooArray[0];
fooArray[0] = 127;
System.out.println("foo is: " + foo);
... imprimera
foo is: 1
... ce qui est bien. La valeur est copiée dans une variable finale et ne peut plus être modifiée. Jouer avec la valeur dans le tableau ne change pas la valeur de foo
(comme prévu ...).
Pourquoi les éléments suivants nécessitent-ils un casting?
final byte[] fooArray = new byte[1];
fooArray[0] = 1;
final byte foo = fooArray[0];
final byte fooFoo = foo + foo;
En quoi est-ce différent du deuxième exemple de cette question? Pourquoi le compilateur me donne-t-il l'erreur suivante?
Erreur: (5, 27) Java: types incompatibles: possible conversion avec perte de l'int en octet
Comment cela peut-il arriver?
Le JLS ( §5.2 ) a des règles spéciales pour la conversion des affectations avec expressions constantes:
De plus, si l'expression est une expression constante ( §15.28 ) de type
byte
,short
,char
ouint
:
- Une conversion primitive rétrécissante peut être utilisée si le type de la variable est
byte
,short
ouchar
, et que la valeur de l'expression constante est représentable dans le type de la variable variable.
Si nous suivons le lien ci-dessus, nous les voyons dans la définition de expression constante:
Si nous suivons le deuxième lien ci-dessus, nous voyons que
Une variable de type primitif ou de type
String
, c'est-à-direfinal
et initialisée avec une expression constante à la compilation ( §15.28 ), est appelée a variable constante.
Il s'ensuit que foo + foo
peut uniquement être affecté à fooFoo
si foo
est une variable constante. Pour appliquer cela à vos cas:
byte foo = 1;
ne définit pas une variable constante car ce n'est pas final
.
final byte foo = 1;
définit une variable constante, car c'est final
et initialisé avec un expression constante (un littéral primitif).
final byte foo = fooArray[0];
ne définit pas une variable constante car elle n'est pas initialisée avec une expression constante.
Notez que si fooFoo
est lui-même final
n'a pas d'importance.
La valeur 1 s'intègre parfaitement dans un octet; 1 + 1 aussi; et lorsque la variable est finale, le compilateur peut faire pliage constant . (en d'autres termes: le compilateur n'utilise pas foo
lors de cette opération +; mais les valeurs "brutes" 1)
Mais lorsque la variable n'est pas définitive, toutes les règles intéressantes sur les conversions et les promotions entrent en jeu (voir ici ; vous voulez lire la section 5.12 sur l'élargissement des conversions primitives).
Pour la deuxième partie: rendre un tableau final vous permet toujours de changer n'importe lequel de ses champs; donc encore; aucun pliage constant possible; de sorte que l'opération "d'élargissement" se déclenche à nouveau.
C'est en effet ce que le compilateur fait en repliement constant lorsqu'il est utilisé avec final
, comme nous pouvons le voir dans le code octet:
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'
Et si vous changez votre octet final en 127, il se plaindra également:
final byte s = 127;
byte ss = s + s;
dans ce cas, le compilateur calcule le résultat et le connaît hors limites, il se plaindra donc toujours de leur incompatibilité.
Plus:
Et ici est une autre question sur le pliage constant avec une chaîne: