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

moh*_*med 34 java casting type-conversion implicit-conversion narrowing

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

byte byteValue = 2;
Run Code Online (Sandbox Code Playgroud)

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

byte byteValue = 4/2;
Run Code Online (Sandbox Code Playgroud)

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

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

int n1 = 4;
byte value = n1/2;
Run Code Online (Sandbox Code Playgroud)

或在此

byte n1 = 4;
byte value = n1/2;
Run Code Online (Sandbox Code Playgroud)

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

Zab*_*uza 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;
Run Code Online (Sandbox Code Playgroud)

无损转换

您将收到示例 3示例 4示例 5 中提到的编译时错误。

首先,示例 1 到 4 的简单数学运算是在编译时执行的。因此 Java 将500 / 2在编译时进行计算并将代码替换为 basic 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);
Run Code Online (Sandbox Code Playgroud)

产生的值-6


常量表达式

你的最后一个例子很有趣:

int n1 = 4;
byte byteValue = n1/2;
Run Code Online (Sandbox Code Playgroud)

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

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

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

final int n1 = 4;
byte byteValue = n1/2;
Run Code Online (Sandbox Code Playgroud)

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

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

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


Ekl*_*vya 5

文档

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

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

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

byte byteValue = 2;
byte byteValue = 4/2;
Run Code Online (Sandbox Code Playgroud)

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

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

byte n1 = 4;
byte value = n1/2;
Run Code Online (Sandbox Code Playgroud)


Les*_*iak 1

https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.2中对此进行了描述

\n
\n

另外,如果表达式是 byte、short、char 或 int 类型的常量表达式 (\xc2\xa715.28):

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

结果太大:

\n
byte byteValue = 100000000/2;\n\nerror: incompatible types: possible lossy conversion from int to byte\n
Run Code Online (Sandbox Code Playgroud)\n

最终变量作为操作数:

\n
final byte n1 = 4;\nbyte value = n1/2;\n
Run Code Online (Sandbox Code Playgroud)\n

  • Java中有一个特殊的关键字“final”,它表示一个不能被重新赋值的变量。看我的第二个例子 (2认同)