隐式转换在 Java 中是如何工作的?

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

byte byteValue = 2;

Java 自动将文本值 2(缺省情况下为 int)转换为字节。如果我写,同样的事情也有效

byte byteValue = 4/2;

RHS 作为整型进行评估,并隐式转换为字节。

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

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

或在此

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

我知道这两个示例的 RHS 都作为 int 进行评估。但是为什么Java不像前两种情况那样将其隐式转换为字节。是否仅当存在文本时才隐式转换为较小的数据类型?


答案 1

解释

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

// 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 / 2byte byteValue = 250;

Java 中字节的有效值为 。因此,超出该范围的任何值都不能仅被视为一个值,而是需要显式转换。因此,示例 1示例 2 通过。-128127byte


有损窄转换

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

它说从 到 的转换(如果它超出 范围)是一个缩小的基元转换并且它可能会丢失信息(出于明显的原因)。它继续解释转换是如何完成的:intbytebyte

将有符号整数缩小到整数类型 T 的转换只会丢弃除 n 个最低阶位之外的所有位,其中 n 是用于表示类型 T 的位数。除了可能丢失有关数值大小的信息外,这还可能导致结果值的符号与输入值的符号不同。

从第二章开始,如果值是常量表达式,则允许使用具有窄转换的赋值

此外,如果表达式是 类型为 、 short、char 或 int 的常量表达式 (§15.29):byte

如果变量的类型为 short 或 char,并且常量表达式的值可在变量的类型中表示,则可以使用窄基元转换。byte

长话短说,必须向Java明确宣布可能丢失信息的缩小转换(因为值超过范围)。Java不会在你强迫的情况下为你做这件事。这是由演员完成的。

例如

byte byteValue = (byte) (500 / 2);

得到值 。-6


常量表达式

你的最后一个例子非常有趣:

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

虽然这不会超过范围,但Java仍然将其视为有损缩小转换。为什么会这样呢?

好吧,Java无法保证在执行前一秒100%不变。因此,它必须考虑您的所有代码,以查看是否有人偷偷摸摸地访问并更改它。Java 在编译时不执行此类分析。n1n1/2n1

因此,如果你能告诉Java,它留下来并且实际上永远不会改变,那么这实际上就会编译。在这种特定情况下,制作它就足够了 。所以与n14final

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

它实际上会编译,因为Java知道它留下来并且不能再改变。因此,它可以在编译时计算并替换代码基本上在范围内。n14n1/22byte byteValue = 2;

因此,您创建了一个常量表达式,如前面在 5.2 中所述。分配上下文n1 / 2

您可以检查在15.29中具有常量表达式所需的详细信息。常量表达式。基本上,一切都很简单,可以很容易地计算到位,没有任何方法调用或其他花哨的东西。


答案 2

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

分配上下文允许使用以下方法之一:

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

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

  • 如果变量的类型为 、 或 ,并且常量表达式的值在变量的类型中是可表示的,则可以使用缩小基元转换。byteshortchar

从到的转换是一种缩小的基元转换。intbyte

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

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

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

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