Starting with the following code...
byte foo = 1;
byte fooFoo = foo + foo;
When I try compiling this code I will get the following error...
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
, orint
:
- A narrowing primitive conversion may be used if the type of the variable is
byte
,short
, orchar
, 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 isfinal
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.
This occurs because of
byte foo = 1;
byte fooFoo = foo + foo;
foo + foo = 2 will be answered but 2 is not byte type because of java has default data type to integer variables it's type is int. So you need to tell the compiler by force that answer must be a byte type explicitly.
class Example{
public static void main(String args[]){
byte b1 = 10;
byte b2 = 20;
byte b1b2 = (byte)(b1 + b2);
//~ b1 += 100; // (+=) operator automaticaly type casting that means narrow conversion
int tot = b1 + b2;
//~ this bellow statement prints the type of the variable
System.out.println(((Object)(b1 + b2)).getClass()); //this solve your problem
}
}
The value 1 fits nicely into a byte; so does 1+1; and when the variable is final, the compiler can do constant folding. (in other words: the compiler doesn't use foo
when doing that + operation; but the "raw" 1 values)
But when the variable is not final, then all the interesting rules about conversions and promotions kick in (see here; you want to read section 5.12 about widening primitive conversions).
For the second part: making an array final still allows you to change any of its fields; so again; no constant folding possible; so that "widening" operation is kicking in again.
It is indeed what compiler do in constant folding when used with final
, as we can see from 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'
And if you change your final byte to 127, it will also complain:
final byte s = 127;
byte ss = s + s;
in which cases, the compiler compute the result and know it out of limit, so it will still complain they are incompatible.
More:
And here is another question about constant folding with string: