【问题标题】:How does implicit conversion work in Java?Java 中的隐式转换是如何工作的?
【发布时间】:2020-11-07 23:01:15
【问题描述】:

我知道在 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不像前两种情况那样将其隐式转换为字节。是否只有在有字面量的情况下才会隐式转换为较小的数据类型?

【问题讨论】:

    标签: java casting type-conversion implicit-conversion narrowing


    【解决方案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 / 2 并将代码替换为基本上 byte byteValue = 250;

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


    有损缩小转换

    要了解其余部分失败的原因,我们必须研究 Java 语言规范 (JLS),更具体的章节 5.1.3. Narrowing Primitive Conversion5.2. Assignment Contexts

    它表示从intbyte 的转换(如果它超出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 中查看需要有一个常量表达式的详细信息。基本上所有简单的东西都可以轻松计算,无需任何方法调用或其他花哨的东西。

    【讨论】:

      【解决方案2】:

      来自doc

      此外,如果表达式是常量表达式(§15.28) 输入 byte、short、char 或 int:

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

      所以对于你的前两种情况,值是常量,为什么它的值可以表示为变量类型byte

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

      对于n1 的后两种情况,这里的n1/2 不是constant expression,因此无法进行转换。所以n1/2的值不能用变量byte的类型来表示。

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

      【讨论】:

        【解决方案3】:

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

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

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

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

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

        intbyte 的转换是一种窄化原语转换。

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

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

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

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

        【讨论】:

          【解决方案4】:

          这在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 变量作为操作数:

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

          【讨论】:

          • 我不明白你所说的final variables是什么意思?
          • Java中有一个特殊的关键字final,表示一个不能重新赋值的变量。看我的第二个例子
          • 实际上,将n1 声明为final 不足以使n1/2 成为编译时间常数。 n1 变量也需要为 static
          猜你喜欢
          • 1970-01-01
          • 2021-02-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多