34

我知道在 Java 中整数文字默认是int的,所以如果我写这样的东西

byte byteValue = 2;

Java 自动将文字值2(默认为 int)转换为字节。如果我写同样的事情

byte byteValue = 4/2;

RHS 被评估为 int 并隐式转换为字节。

但是为什么在以下两种情况下不会发生隐式转换呢?

int n1 = 4;
byte value = n1/2;

或者在这个

byte n1 = 4;
byte value = n1/2;

我知道这两个示例的 RHS 都被评估为int。但是为什么Java不像前两种情况那样将其隐式转换为字节。只有在有文字的情况下才会隐式转换为较小的数据类型吗?

4

4 回答 4

30

解释

让我们看一下您的代码和一些修改后的示例:

// 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 中字节的有效值为-128to 127。因此,任何超出该范围的值都不能仅仅被视为 a byte,而是需要显式转换。因此,示例 1示例 2通过。


有损缩小转换

要理解为什么其余的都失败了,我们必须研究 Java 语言规范(JLS),更具体地说是第5.1.3 章。缩小原始转换5.2。分配上下文

它说从intto的转换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 不会在编译时进行这种分析。

因此,如果您可以告诉 Javan1保持不变4并且实际上永远不会改变,那么它实际上会编译。在这种特定情况下,它就足够了final。所以随着

final int n1 = 4;
byte byteValue = n1/2;

它实际上会编译,因为 Java 知道它会n1保留4并且不能再更改。因此,它可以n1/2在编译时计算2并用 basic 替换代码byte byteValue = 2;,这在范围内。

所以你做n1 / 2了一个常量表达式,就像之前在5.2 中解释的那样。分配上下文

您可以查看15.29 中需要有一个常量表达式的详细信息。常量表达式。基本上所有简单的东西都可以很容易地计算出来,而无需任何方法调用或其他花哨的东西。

于 2020-07-18T08:05:16.323 回答
5

来自文档

此外,如果表达式是byte、short、char 或 int 类型的常量表达式(第 15.28 节):

如果变量的类型是 byte、short 或 char,并且常量 表达式的值可以用变量的类型表示,则可以使用窄化原语转换。

因此,对于您的前两种情况 value 是恒定的,为什么它的 value 可以表示为变量 type byte

byte byteValue = 2;
byte byteValue = 4/2;

而对于后两种情况 for n1,这里n1/2不是 a constant expression,所以转换不可用。所以 value ofn1/2不能用变量的类型表示byte

int n1 = 4;
byte value = n1/2;

byte n1 = 4;
byte value = n1/2;
于 2020-07-18T08:20:58.290 回答
4

这记录在Java 语言规范的§5.2中。该部分讨论了在赋值上下文中允许进行哪些转换,例如byte byteValue = n1/2;.

赋值上下文允许使用以下之一:

  • ...
  • ...(与问题无关的转换)

此外,如果表达式是 、 、 或 类型的常量表达式(15.28 节):byteshortcharint

  • 如果变量的类型是 、 或 ,并且常量表达式的值可以用变量的类型表示,则可以使用窄化byteshortchar转换

from intto的转换byte是一种窄化的原始转换。

在这些情况下,右侧的表达式都是常量表达式,即编译器可以在编译时计算的表达式:

byte byteValue = 2;
byte byteValue = 4/2;

因此应用了转换并编译了代码。

您可以在§15.28中确切地看到什么构成了常量表达式。你会看到,如果一个表达式有一个final像 in 这样的非变量n1/2,它就不是一个常量表达式。编译器不想分析/运行/跟踪您的代码以找出n1. 因此,转换不可用且代码无法编译。

于 2020-07-18T08:07:09.277 回答
1

这在https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.2中有描述

此外,如果表达式是 byte、short、char 或 int 类型的常量表达式(第 15.28 节):

  • 如果变量的类型是 byte、short 或 char,并且常量表达式的值可以用变量的类型表示,则可以使用窄化原语转换。

结果太大:

byte byteValue = 100000000/2;

error: incompatible types: possible lossy conversion from int to byte

最终变量作为操作数:

final byte n1 = 4;
byte value = n1/2;
于 2020-07-18T07:34:31.013 回答