为什么java在没有接受long的方法时将long参数提升为float/double?

2022-09-01 10:37:00

这是一个SSCCE,它演示了所描述的(恕我直言,奇怪的)行为:

public class Test {

   public static void print(int param) {
       System.out.println("int");
   }

   public static void print(float param) {
       System.out.println("float");
   }

   public static void print(Long param) { //<--Wrapper type
       System.out.println("Long");
   }
   public static void main(String[] args) {
       long param = 100L;
       print(param);  // output == float
   }
} 

为什么Java会这样做?


答案 1

Java语言规范对此非常清楚(强调我的):

15.12.2 编译时步骤 2:确定方法签名

[...]

  1. 第一阶段(§15.12.2.2)执行过载解析,而不允许装箱或取消装箱转换[...]如果在此阶段找不到适用的方法,则处理将继续到第二阶段。[...]

  2. 第二阶段(§15.12.2.3)执行过载解析,同时允许装箱和拆箱[...]

  3. 第三阶段 (§15.12.2.4) 允许将重载与可变 arity 方法、装箱和取消装箱相结合。

也就是说,在第一步中只是和可以合适的。后者匹配,没有进一步调查。print(int)print(float)


JLS中也解释了这些规则的原因:

这保证了在 Java SE 5.0 之前的 Java 编程语言中有效的任何调用都不会因为引入变量 arity 方法、隐式装箱和/或取消装箱而被视为模棱两可。

想象一下,你的类是针对 Java 1.4 编译的(在自动装箱之前)。在这种情况下,很明显:必须选择(假设我们同意为什么被认为是安全的,并且可以是隐含的......),因为与论证完全不相容。Testprint(float)longfloatprint(Long)long

稍后,您针对 Java 5+ 编译相同的代码。编译器可以:

  • 在这种情况下选择更“明显”。因此,在升级到Java 5后,您的程序的行为会有所不同...print(Long)

  • 产生编译错误,因为调用不明确。因此,以前正确的代码不再在Java 5下编译(AFAIR从来都不是这种情况)。

  • ...或保留旧的语义并调用与Java 1.4相同的方法

您现在应该了解为什么使用 - 因为它将在Java 1.4下选择。Java必须向后兼容。print(float)


答案 2

它选择的原因是自动装箱是后来添加的,并且出于向后兼容性的原因,它必须进行与它一直以来相同的调用。floatLong