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

Kor*_*gay 57 java arrays int byte casting

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

byte foo = 1;
byte fooFoo = foo + foo;
Run Code Online (Sandbox Code Playgroud)

当我尝试编译此代码时,我将收到以下错误...

错误:(5,27)java:不兼容的类型:从int到byte的可能有损转换

......但如果foo是最终的......

final byte foo = 1;
final byte fooFoo = foo + foo;
Run Code Online (Sandbox Code Playgroud)

该文件将成功编译.

转到以下代码......

final byte[] fooArray = new byte[1];
fooArray[0] = 1;

final byte foo = fooArray[0];
fooArray[0] = 127;

System.out.println("foo is: " + foo);
Run Code Online (Sandbox Code Playgroud)

......会打印出来的

foo is: 1
Run Code Online (Sandbox Code Playgroud)

......很好.该值将复制到最终变量,不能再更改.使用数组中的值不会更改foo(正如预期的那样...)的值.

为什么以下需要演员?

final byte[] fooArray = new byte[1];
fooArray[0] = 1;
final byte foo = fooArray[0];
final byte fooFoo = foo + foo;
Run Code Online (Sandbox Code Playgroud)

这与这个问题中的第二个例子有什么不同?为什么编译器会给我以下错误?

错误:(5,27)java:不兼容的类型:从int到byte的可能有损转换

怎么会发生这种情况?

shm*_*sel 45

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

此外,如果表达式是一个常量表达式(§15.28类型的)byte,short,char,或int:

  • 如果变量的类型是byte,, short或者char,则可以使用缩小的基元转换,并且常量表达式的值可以在变量的类型中表示.

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

  • 原始类型的文字和类型的文字 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是否无关紧要.


Gho*_*ica 17

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

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

对于第二部分:使数组最终仍允许您更改其任何字段; 再一次; 没有恒定的折叠可能; 所以"扩大"的行动再次开始.


Ton*_*ony 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'
Run Code Online (Sandbox Code Playgroud)

如果你将最后一个字节改为127,它也会抱怨:

    final byte s = 127;
    byte ss = s + s;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,编译器计算结果并知道它超出限制,因此它仍然会抱怨它们不兼容.

更多:

这里大约是恒定的折叠用绳子另一个问题:

  • 还演示了动机:当编译为字节码时,`byte`变量被扩展为`int`以便存储它们并用它们进行计算,除非右侧的操作数是`final`常量,在这种情况下计算可以在编译时静态发生. (2认同)