Wednesday, 31 May 2017

Why does Java require an explicit cast on a final variable if it was copied from an array?



Starting with the following code...



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


When I try compiling this code I will get the following error...





Error:(5, 27) java: incompatible types: possible lossy conversion from int to byte




... but if foo is final...



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



the file will compile successfully.



Moving on to the following code...



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

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


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


... will print



foo is: 1


... which is fine. The value is copied to a final variable and it can not be changed any more. Playing with the value in the array does not change the value of the foo (as expected...).




Why does the following require a cast?



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


How is this different than the second example in this question? Why is the compiler giving me the following error?





Error:(5, 27) java: incompatible types: possible lossy conversion from int to byte




How can this happen?


Answer



The JLS (§5.2) has special rules for assignment conversion with constant expressions:




In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:





  • A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.




If we follow the link above, we see these in the definition of constant expression:






  • Literals of primitive type and literals of type String

  • The additive operators + and -

  • Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).




If we follow the second link above, we see that




A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable.





It follows that foo + foo can only be assigned to fooFoo if foo is a constant variable. To apply that to your cases:




  • byte foo = 1; does not define a constant variable because it's not final.


  • final byte foo = 1; does define a constant variable, because it's final and initialized with a constant expression (a primitive literal).


  • final byte foo = fooArray[0]; does not define a constant variable because it's not initialized with a constant expression.





Note that whether fooFoo is itself final doesn't matter.


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...