如果从数组中复制了Java,为什么Java需要对最终变量进行显式强制转换?

时间:2017-03-26 07:32:43

标签: java arrays casting int byte

从以下代码开始...

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的可能有损转换

这怎么可能发生?

4 个答案:

答案 0 :(得分:43)

JLS(§5.2)具有使用常量表达式进行赋值转换的特殊规则

  

此外,如果表达式是byteshortcharint类型的常量表达式§15.28}:

     
      
  • 如果变量的类型是byteshortchar,则可以使用缩小的基元转换,并且常量表达式的值可以在类型中表示。变量
  •   

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

  
      
  • 原始类型的文字和String
  • 类型的文字   
  • 添加剂运算符+-
  •   
  • 引用常量变量(§6.5.6.1)的简单名称(§4.12.4)。
  •   

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

  

基本类型或类型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
    }
}