Comenzando con el siguiente código...
byte foo = 1; byte fooFoo = foo + foo;Cuando intento compilar este código obtendré el siguiente error...
Error: (5, 27) java: tipos incompatibles: posible conversión con pérdida de int a byte
... pero si foo es definitivo...
final byte foo = 1; final byte fooFoo = foo + foo;el archivo se compilará con éxito.
Pasando al siguiente código...
final byte[] fooArray = new byte[1]; fooArray[0] = 1; final byte foo = fooArray[0]; fooArray[0] = 127; System.out.println("foo is: " + foo);... imprimirá
foo is: 1 ... lo cual está bien. El valor se copia a una variable final y ya no se puede cambiar. Jugar con el valor en la matriz no cambia el valor de foo (como se esperaba...).
¿Por qué lo siguiente requiere un yeso?
final byte[] fooArray = new byte[1]; fooArray[0] = 1; final byte foo = fooArray[0]; final byte fooFoo = foo + foo;¿En qué se diferencia esto del segundo ejemplo en esta pregunta? ¿Por qué el compilador me da el siguiente error?
Error: (5, 27) java: tipos incompatibles: posible conversión con pérdida de int a byte
¿Cómo puede suceder esto?
El JLS ( §5.2 ) tiene reglas especiales para la conversión de asignaciones con expresiones constantes :
Además, si la expresión es una expresión constante ( §15.28 ) de tipo
byte,short,charoint:
- Se puede usar una conversión primitiva de restricción si el tipo de variable es
byte,shortochar, y el valor de la expresión constante se puede representar en el tipo de variable.
Si seguimos el enlace de arriba, vemos esto en la definición de expresión constante :
Si seguimos el segundo enlace de arriba, vemos que
Una variable de tipo primitivo o de tipo
String, que esfinale inicializada con una expresión constante en tiempo de compilación ( §15.28 ), se denomina variable constante .
De ello se deduce que foo + foo sólo se puede asignar a fooFoo si foo es una variable constante . Para aplicar eso a sus casos:
byte foo = 1; no define una variable constante porque no es final .
final byte foo = 1; define una variable constante , porque es final e inicializada con una expresión constante (un literal primitivo).
final byte foo = fooArray[0]; no define una variable constante porque no se inicializa con una expresión constante .
Tenga en cuenta que no importa si fooFoo es en sí mismo final .
El valor 1 encaja muy bien en un byte; también 1+1; y cuando la variable es final, el compilador puede hacer un plegado constante . (en otras palabras: el compilador no usa foo al hacer esa operación +, sino los valores 1 "sin procesar")
Pero cuando la variable no es definitiva, entonces entran en juego todas las reglas interesantes sobre conversiones y promociones (consulte aquí ; desea leer la sección 5.12 sobre la ampliación de conversiones primitivas).
Para la segunda parte: hacer que una matriz sea final aún le permite cambiar cualquiera de sus campos; así que de nuevo; no es posible un plegado constante; para que la operación de "ampliación" vuelva a ponerse en marcha.
De hecho, es lo que hace el compilador en el plegado constante cuando se usa con final , como podemos ver en el código de bytes:
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'Y si cambia su byte final a 127, también se quejará:
final byte s = 127; byte ss = s + s;en cuyo caso, el compilador calcula el resultado y lo sabe fuera de límite, por lo que aún se quejará de que son incompatibles.
Más:
Y aquí hay otra pregunta sobre el plegado constante con hilo: