60 votes

Pourquoi Java exige-t-il un cast explicite sur une variable finale si elle a été copiée depuis un tableau ?

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 int à byte

... mais si foo est définitive...

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 elle ne peut plus être modifiée. Jouer avec la valeur dans le tableau ne change pas la valeur de la variable finale. foo (comme prévu...).

Pourquoi les éléments suivants nécessitent-ils un plâtre ?

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

En quoi cela est-il 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 int à byte

Comment cela peut-il arriver ?

47voto

shmosel Points 4840

Le JLS ( §5.2 ) a des règles spéciales pour la conversion d'affectation avec expressions constantes :

En outre, si l'expression est une expression constante ( §15.28 ) de type byte , short , char ou int :

  • Une conversion primitive restrictive peut être utilisée si le type de la variable est byte , short ou char et la valeur de l'expression constante est représentable dans le type de la variable.

Si nous suivons le lien ci-dessus, nous voyons ceux-ci dans la définition de expression constante :

  • Les littéraux de type primitif et les littéraux de type String
  • Les opérateurs additifs + et -
  • Les noms simples ( §6.5.6.1 ) qui font référence à des variables constantes ( §4.12.4 ).

Si nous suivons le deuxième lien ci-dessus, nous voyons que

Une variable de type primitif ou de type String c'est-à-dire final et initialisé avec une expression constante en temps de compilation ( §15.28 ), est appelé un variable constante .

Il s'ensuit que foo + foo ne peut être attribué qu'à fooFoo si foo est un variable constante . Pour appliquer cela à vos cas :

  • byte foo = 1; n'est pas définir un variable constante parce que ce n'est pas final .

  • final byte foo = 1; fait définir un variable constante parce que c'est final et initialisé avec un expression constante (un littéral primitif).

  • final byte foo = fooArray[0]; n'est pas définir un variable constante car il n'est pas initialisé avec un expression constante .

Notez que si fooFoo est lui-même final n'a pas d'importance.

18voto

GhostCat Points 83269

La valeur 1 tient bien dans un octet ; il en va de même pour 1+1 ; et lorsque la variable est finale, le compilateur peut effectuer les opérations suivantes pliage constant . (en d'autres termes, le compilateur n'utilise pas le format foo lors de cette + opération ; mais les valeurs "brutes" 1)

Mais lorsque la variable n'est pas finale, toutes les règles intéressantes concernant les conversions et les promotions entrent en jeu (cf. 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 changement aucun de ses champs ; donc encore une fois, pas de pliage constant possible ; de sorte que l'opération d'"élargissement" recommence.

8voto

Tony Points 3371

C'est en effet ce que fait le compilateur dans le pliage constant lorsqu'il est utilisé avec final comme on peut le voir dans le byte code :

    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 dernier octet en 127, il se plaindra également :

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

dans ces cas, le compilateur calcule le résultat et le sait hors limite, donc il se plaindra quand même qu'ils sont incompatibles.

Plus :

Et ici est une autre question sur le pliage constant avec la ficelle :

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X