从以下代码开始...
byte foo = 1;
byte fooFoo = foo + foo;
当我尝试编译此代码时,我将收到以下错误...
错误:(5,27)java:不兼容的类型:从int到byte的可能有损转换
...但如果foo
是最终的......
final byte foo = 1;
final byte fooFoo = foo + foo;
文件将成功编译。
转到以下代码......
final byte[] fooArray = new byte[1];
fooArray[0] = 1;
final byte foo = fooArray[0];
fooArray[0] = 127;
System.out.println("foo is: " + foo);
...将打印
foo is: 1
......这很好。该值将复制到最终变量,不能再更改。使用数组中的值不会更改foo
的值(正如预期的那样......)。
为什么以下需要演员?
final byte[] fooArray = new byte[1];
fooArray[0] = 1;
final byte foo = fooArray[0];
final byte fooFoo = foo + foo;
这与这个问题中的第二个例子有什么不同?为什么编译器会给我以下错误?
错误:(5,27)java:不兼容的类型:从int到byte的可能有损转换
这怎么可能发生?
答案 0 :(得分:43)
JLS(§5.2)具有使用常量表达式进行赋值转换的特殊规则:
此外,如果表达式是
byte
,short
,char
或int
类型的常量表达式§15.28}:
- 如果变量的类型是
byte
,short
或char
,则可以使用缩小的基元转换,并且常量表达式的值可以在类型中表示。变量
如果我们按照上面的链接,我们会在常量表达式的定义中看到这些:
如果我们按照上面的第二个链接,我们会看到
基本类型或类型
String
的变量,即final
,并使用编译时常量表达式(§15.28)初始化,称为常量变量
如果foo + foo
是常量变量,fooFoo
只能分配给foo
。要将其应用于您的案例:
byte foo = 1;
不定义常量变量,因为它不是final
。
final byte foo = 1;
确定常量变量,因为它是final
并使用常量表达式初始化 >(原始文字)。
final byte foo = fooArray[0];
不定义常量变量,因为它未使用常量表达式进行初始化。
请注意,fooFoo
本身final
是否无关紧要。
答案 1 :(得分:17)
值1非常适合一个字节; 1 + 1也是如此;当变量是final时,编译器可以执行constant folding。 (换句话说:编译器在执行该操作时不使用foo
;但是“raw”1值)
但是当变量不是最终变量时,关于转换和促销的所有有趣规则都会出现(参见here;您希望阅读有关扩大原始转换的第5.12节)。
对于第二部分:使阵列最终仍允许您更改其任何字段;再一次;没有恒定的折叠可能;所以“扩大”的行动再次开始。
答案 2 :(得分:7)
与final
一起使用时,编译器在常量折叠中的作用确实如此,我们可以从字节代码中看到:
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'
如果你将最后一个字节改为127,它也会抱怨:
final byte s = 127;
byte ss = s + s;
在这种情况下,编译器会计算结果并知道它超出限制,因此它仍会抱怨它们不兼容。
更多:
here是关于使用字符串进行常量折叠的另一个问题:
答案 3 :(得分:0)
这是由于
byte foo = 1;
byte fooFoo = foo + foo;
foo + foo = 2将被回答,但是2不是字节类型,因为java具有默认的数据类型为整数变量,其类型为int。所以你需要强制告诉编译器 该答案必须明确地是字节类型。
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
}
}