为什么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式是byteshortcharinttypes的常量expression式(第15.28节 ):

  • 如果variables的types是byteshortchar ,并且常量expression式的值可以用variables的types表示,则可以使用缩小的原始转换。

如果我们按照上面的链接,我们在常量expression式的定义中看到这些:

  • 原始types文字和Stringtypes的文字
  • 添加剂操作符+-
  • 简单名称( §6.5.6.1 )引用常量variables( §4.12.4 )。

如果我们按照上面的第二个链接,我们可以看到

原始types或types为Stringvariables是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常量折叠的问题: