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

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

I know that in Java Integer literals are int by default,so if I write something like this

byte byteValue = 2;

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

Java auto converts the literal value 2(which is an int by default) to byte. And the same thing works if I write

byte byteValue = 4/2;

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

The RHS is evaluated as an int and implicitly converted to a byte.

但是为什么下面两种情况没有发生隐式转换呢?

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

或在此

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

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

I know that the RHS of both these examples are evaluated as an int. But why doesn't Java convert it to a byte implicitly like it did in the first two cases.Does implicit conversion to smaller data type happen only if there are literals?

推荐答案

说明

让我们看看你的代码和一些修改过的例子:

Explanation

Lets take a look at your code and some modified examples:

// 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 会出现上述编译时错误.


Non-lossy conversion

You will get the mentioned compile-time error for Example 3, Example 4 and Example 5.

首先,示例 1 到 4 的简单数学运算是在编译时执行的.因此 Java 将在编译时计算 500/2 并将代码替换为基本上 byte byteValue = 250;.

First of all, the simple math you have for Example 1 to 4 is executed at compile-time. So Java will compute 500 / 2 at compile-time and replace the code with basically byte byteValue = 250;.

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

Valid values for bytes in Java are -128 to 127. So any value outside of that range can not just be taken as a byte but requires explicit conversion. Because of that, Example 1 and Example 2 pass.

要了解其余部分失败的原因,我们必须研究 Java 语言规范 (JLS),更具体的章节 5.1.3.缩小原始转换范围和5.2.分配上下文.

To understand why the rest fails, we have to study the Java Language Specification (JLS), more specifically chapter 5.1.3. Narrowing Primitive Conversion and 5.2. Assignment Contexts.

它表示从 intbyte 的转换(如果它超出 byte 的范围)是一个缩小原始转换,并且它可能会丢失信息(原因很明显).它继续解释转换是如何完成的:

It says that a conversion from int to byte (if it is outside of the range of byte) is a narrowing primitive conversion and that it may lose information (for obvious reasons). It continues by explaining how the conversion is done:

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

A narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits, where n is the number of bits used to represent type T. In addition to a possible loss of information about the magnitude of the numeric value, this may cause the sign of the resulting value to differ from the sign of the input value.

从第二章开始,如果值为常量表达式,则允许进行窄转换的赋值.

From the second chapter, assignments with narrow conversions are allowed if the value is a constant expression.

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

In addition, if the expression is a constant expression (§15.29) of type byte, short, char, or int:

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

A narrowing primitive conversion may be used if the variable is of type byte, short, or char, and the value of the constant expression is representable in the type of the variable.

长话短说,可能会丢失信息(因为值超出范围)的缩小转换必须明确地通知 Java.Java 不会在您不强制的情况下为您完成它.这是由演员完成的.

Long story short, a narrowing conversion that may lose information (because the value exceeds the range) has to explicitly be announced to Java. Java will not just do it for you without you forcing it. That is done by a cast.

例如

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

导致值 -6.

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

Your last example is very interesting:

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

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

Although this does not exceed the range, Java still treats it as lossy narrowing conversion. Why is that the case?

好吧,Java 不能 100% 保证 n1 在执行 n1/2 之前的最后一秒没有改变.因此,它必须考虑您的所有代码,以查看是否有人偷偷访问 n1 并对其进行更改.Java 不会在编译时进行这种分析.

Well, Java can not ensure 100% that n1 is not changed last second before n1/2 is executed. Therefore, it would have to consider all of your code to see if maybe someone accesses n1 sneaky and changes it. Java does not do this kind of analysis at compile-time.

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

So if you can tell Java that n1 stays 4 and can actually never change, then this will actually compile. In this specific case, it would be enough to make it final. So with

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

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

it will actually compile because Java knows that n1 stays 4 and can not change anymore. Hence it can compute n1/2 at compile-time to 2 and replace the code with basically byte byteValue = 2;, which is in range.

所以你把 n1/2 做成了一个常量表达式,正如之前在 5.2.分配上下文.

So you made n1 / 2 a constant expression, as explained before in 5.2. Assignment Contexts.

您可以在 15.29.常量表达式.基本上所有简单的东西都可以轻松计算,无需任何方法调用或其他花哨的东西.

You can check the details what it needs to have a constant expression in 15.29. Constant Expressions. Basically everything simple that can easily be computed in place without any method invocations or other fancy stuff.

相关文章