说明
让我们看看你的代码和一些修改过的例子:
// Example 1
byte byteValue = 2;
// Example 2
byte byteValue = 4/2;
// Example 3
byte byteValue = 2000;
// Example 4
byte byteValue = 500/2;
// Example 5
int n1 = 4;
byte byteValue = n1/2;
无损转换
示例 3、示例 4 和 示例 5 会出现上述编译时错误。
首先,示例 1 到 4 的简单数学运算是在编译时执行的。因此 Java 将在编译时计算 500 / 2 并将代码替换为基本上 byte byteValue = 250;。
Java 中字节的有效值为-128 到127。因此,任何超出该范围的值都不能仅仅被视为byte,而是需要显式转换。因此,示例 1 和 示例 2 通过。
有损缩小转换
要了解其余部分失败的原因,我们必须研究 Java 语言规范 (JLS),更具体的章节 5.1.3. Narrowing Primitive Conversion 和 5.2. Assignment Contexts。
它表示从int 到byte 的转换(如果它超出byte 的范围)是缩小原始转换,它可能会丢失信息(原因很明显)。它继续解释转换是如何完成的:
有符号整数到整数类型 T 的窄化转换只会丢弃除 n 个最低位之外的所有位,其中 n 是用于表示类型 T 的位数。除了可能丢失有关数值,这可能会导致结果值的符号与输入值的符号不同。
从第二章开始,如果值为常量表达式,则允许进行窄转换的赋值。
此外,如果表达式是 byte、short、char 或 int 类型的常量表达式(第 15.29 节):
如果变量是byte、short 或 char 类型,并且常量表达式的值可以用变量的类型表示,则可以使用缩小原语转换。
长话短说,可能会丢失信息(因为值超出范围)的缩小转换必须明确地通知 Java。 Java 不会在您不强制的情况下为您完成它。这是由演员完成的。
例如
byte byteValue = (byte) (500 / 2);
产生值-6。
常量表达式
你的最后一个例子很有趣:
int n1 = 4;
byte byteValue = n1/2;
虽然这没有超出范围,但Java仍然将其视为有损缩小转换。为什么会这样?
好吧,Java 不能 100% 保证 n1 在执行 n1/2 之前的最后一秒没有改变。因此,它必须考虑你的所有代码,看看是否有人偷偷访问了n1 并对其进行了更改。 Java 不会在编译时进行这种分析。
因此,如果您可以告诉 Java n1 保持 4 并且实际上永远不会改变,那么这将实际编译。在这种特定情况下,将其设为final 就足够了。所以与
final int n1 = 4;
byte byteValue = n1/2;
它实际上会编译,因为 Java 知道 n1 保持 4 并且不能再更改。因此,它可以在编译时将n1/2 计算为2,并将代码替换为基本上在范围内的byte byteValue = 2;。
所以你将n1 / 2 设为常量表达式,正如之前在5.2. Assignment Contexts 中所解释的那样。
您可以在15.29. Constant Expressions 中查看需要有一个常量表达式的详细信息。基本上所有简单的东西都可以轻松计算,无需任何方法调用或其他花哨的东西。