首页 文章

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

提问于
浏览
56

从以下代码开始......

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

怎么会发生这种情况?

3 回答

  • 7

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

    此外,如果表达式是byte,short,char或int类型的常量表达式(第15.28节):如果变量的类型是byte,short或char,则可以使用缩小的原语转换 . 常量表达式的变量可以表示在变量的类型中 .

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

    原始类型的文字和String类型的文字加法运算符和 - 引用常量变量的简单名称(第6.5.6.1节)(第4.12.4节) .

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

    一个原始类型或类型String的变量,它是final的,用编译时常量表达式(第15.28节)初始化,称为常量变量 .

    因此,如果 foo 是常量变量,则 foo + foo 只能分配给 fooFoo . 要将其应用于您的案例:

    • byte foo = 1; does not 定义一个常量变量,因为它不是 final .

    • final byte foo = 1; does 定义一个常量变量,因为它是 final 并用常量表达式(原始文字)初始化 .

    • final byte foo = fooArray[0]; does not 定义一个常量变量,因为它没有用常量表达式初始化 .

    请注意, fooFoo 本身是否 final 无关紧要 .

  • 17

    值1非常适合一个字节; 1 1也是如此;当变量为final时,编译器可以执行constant folding . (换句话说:编译器在执行该操作时不使用 foo ;但是"raw" 1值)

    但是当变量不是最终变量时,关于转换和促销的所有有趣规则都会出现(参见here;您希望阅读有关扩大原始转换的第5.12节) .

    对于第二部分:使数组最终仍然允许你 change 任何字段;再一次;没有恒定的折叠可能;这样"widening"操作又开始了 .

  • 43

    当与 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是关于使用字符串进行常量折叠的另一个问题:

相关问题