为什么一个final变量不总是一个常量表达式?

45
在下面的代码中:
final int a;
a=2;
byte b=a;   // error: possible loss of precision

我为什么会遇到这个错误?a不是一个final变量的编译时常量表达式吗,在赋值期间隐式缩小为byte?

换句话说,上面的代码不等同于:

final int a=2;
byte b=a;
3个回答

51

来自JLS

空白final是一个声明缺乏初始化器的final变量。

常量变量是一个基本类型或类型为Stringfinal变量,其用常量表达式(§15.28)初始化。

您的变量

final int a;

是一个未初始化的final变量。由于它在声明时没有被初始化,因此第二段不适用于它。因此它不是一个常量表达式。

这也适用于字段(fields)。


43

编译器并不是那么聪明。

我们知道这个值总是2。但是如果我们有像这样的东西呢?

class ABC{
    final int a;

    public ABC(){
       if(Math.random() < .5){
          a = 2;
       }
       else{
          a = 12345;
       }

       byte b = a;
    }
}
编译器无法智能区分这两种情况,因此会给出错误提示。

31
实际上,编译器不被允许那么聪明。 - usr
@usr 那是一些严肃的原始宗教信仰。说真的:为什么?编译器在乘法时对数字 124 等非常敏感。这个答案中的情况显然是无法解决的,因为从 random 返回的值取决于程序启动的时间。 - LyingOnTheSky
9
即使计算是确定的,编译器能够解决它,它仍然不能将其视为常量表达式(从而不需要强制转换)。重要的是,一个有效的Java程序是否合法不应该取决于特定编译器的智能程度。 - CodesInChaos
1
@CodesInChaos 你说得对,编译器应该遵循标准。 - LyingOnTheSky
3
基本没错。在C++中,编译器通常会添加自己的扩展功能。在Java中,这基本上是被禁止的(参见微软的诉讼)。 - user253751

2

由于final变量可以被延迟初始化,编译器无法确定在case分支中b是否有值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接