为什么Java需要对最终variables进行显式强制转换,如果从数组中复制的话?
从下面的代码开始…
byte foo = 1; byte fooFoo = foo + foo;
当我尝试编译此代码时,我将得到以下错误…
错误:(5,27)java:incompatible types:可能有损转换从int到字节
…但如果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
…这很好。 该值被复制到最终variables,不能再被改变。 使用数组中的值不会更改foo
的值(如预期的那样)。
为什么以下需要演员?
final byte[] fooArray = new byte[1]; fooArray[0] = 1; final byte foo = fooArray[0]; final byte fooFoo = foo + foo;
这与这个问题中的第二个例子有什么不同? 为什么编译器给我下面的错误?
错误:(5,27)java:incompatible types:可能有损转换从int到字节
这怎么可能发生?
JLS( §5.2 )具有用常量expression式进行赋值转换的特殊规则:
另外,如果expression式是
byte
,short
,char
或int
types的常量expression式(第15.28节 ):
- 如果variables的types是
byte
,short
或char
,并且常量expression式的值可以用variables的types表示,则可以使用缩小的原始转换。
如果我们按照上面的链接,我们在常量expression式的定义中看到这些:
- 原始types文字和
String
types的文字- 添加剂操作符
+
和-
- 简单名称( §6.5.6.1 )引用常量variables( §4.12.4 )。
如果我们按照上面的第二个链接,我们可以看到
原始types或types为
String
variables是final
并且使用编译时常量expression式(第15.28节 )进行初始化,该variables称为常量variables 。
因此,如果foo
是一个常量variables ,则foo + foo
只能分配给fooFoo
。 要将其应用于您的案例:
-
byte foo = 1;
没有定义一个常量variables,因为它不是final
。 -
final byte foo = 1;
没有定义一个常量variables ,因为它是final
并且用一个常量expression式 (一个原始文字)进行初始化。 -
final byte foo = fooArray[0];
没有定义一个常量variables,因为它没有用常量expression式进行初始化。
请注意, fooFoo
本身是否是final
并不重要。
值1很好地适合于一个字节; 所以1 + 1; 当variables是最终的时候,编译器可以做不断的折叠 。 (换句话说,编译器在执行该操作时不使用foo
,而是使用“raw”1值)
但是,当variables不是最终的时候,所有有关转换和促销的有趣规则就会被引入(见这里 ;你想阅读关于扩大原始转换的章节5.12)。
对于第二部分:制作数组最终仍然允许您更改其任何字段; 再一次 不可能持续折叠; 所以“扩大”的操作又在踢。
这是真正的编译器在与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;
在这种情况下,编译器计算结果并知道它超出限制,所以它仍然会抱怨它们不兼容。
更多:
这里还有一个关于string常量折叠的问题: