这真的是加宽与自动装箱吗?

2022-09-01 11:01:06

我在回答另一个问题时看到了这一点,参考了Java规范的缺点:

还有更多的缺点,这是一个微妙的话题。看看这个

public class methodOverloading{
     public static void hello(Integer x){
          System.out.println("Integer");
     }

     public static void hello(long x){
          System.out.println("long");
     }

     public static void main(String[] args){
         int i = 5;
         hello(i);
     }
}

这里将打印“long”(我自己没有检查过),因为编译器选择加宽而不是自动装箱。使用自动装箱时要小心,否则根本不使用它!

我们确定这实际上是一个加宽而不是自动装箱的例子,还是完全是别的什么?

在我最初的扫描中,我同意这样一种说法,即输出将是“长”的,因为被声明为基元而不是对象。但是,如果您更改了i

hello(long x)

hello(Long x)

输出将打印“整数”

这到底是怎么回事?我对java的编译器/字节码解释器一无所知...


答案 1

在第一种情况下,您发生了扩大的转换。当在编译的类上运行“javap”实用程序(包括JDK)时,可以看到这一点:

public static void main(java.lang.String[]);
  Code:
   0:   iconst_ 5
   1:   istore_ 1
   2:   iload_ 1
   3:   i2l
   4:   invokestatic    #6; //Method hello:(J)V
   7:   return

}

显然,您会看到 I2L,它是加宽整数到长字节码指令的助记符。请参阅此处的参考。

在另一种情况下,将“long x”替换为对象“Long x”签名,您将在main方法中包含以下代码:

public static void main(java.lang.String[]);
  Code:
   0:   iconst_ 5
   1:   istore_ 1
   2:   iload_ 1
   3:   invokestatic    #6; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   6:   invokestatic    #7; //Method hello:(Ljava/lang/Integer;)V
   9:   return

}

所以你看到编译器已经创建了一个指令 Integer.valueOf(int),将原语框在包装器内。


答案 2

是的,在测试中尝试一下。你会看到“长”打印。它正在扩大,因为Java会选择在选择将其自动框为整数之前将int扩大为很长一段时间,因此选择hello(long)方法进行调用。

编辑:被引用的原始帖子

进一步编辑:第二个选项打印整数的原因是因为没有“扩大”到一个更大的基元作为选项,所以它必须将其装箱,因此整数是唯一的选择。此外,java只会自动框到原始类型,所以如果你离开hello(Long)并删除hello(Integer),它会给出编译器错误。