Java 重载 - 长和浮动

2022-09-02 12:50:47

我试图理解java重载规则。一切似乎都很好,除了以下,

public static void main(String[] args) {
    long aLong = 123L;        
    foo(aLong);
}

private static void foo(double aDouble) {
    System.out.println("Foo aDouble");
}

private static void foo(Long aWrapperLong) {
    System.out.println("Foo Wrapper Long");
}

private static void foo(int anInt) {
    System.out.println("Foo Int");
}

private static void foo(float aFloat) {
    System.out.println("Foo Float");
}

为什么调用解析为 。我从JLS了解到以下内容,foo(float aFloat)

此步骤使用方法的名称和参数表达式的类型来查找既可访问又适用的方法 可能有多个这样的方法,在这种情况下,将选择最具体的方法。

我在这里故意使用了 Wrapper Long,而不是原始的 long。原始长为64位大小不会最终进入32位浮点数。foo(double aDouble)foo(float aFloat)

为什么Java隐式(不强制转换)将“long”转换为“float”这个问题进一步澄清了这个问题的答案。


答案 1

这是因为 JLS #15 中的“最具体”规则,它又引用了 JLS #4.10,而 JLS #4.10.1 又引用了 #4.10.1,其中指出:

以下规则定义了基元类型之间的直接超类型关系:

  • 双>1浮子

  • 浮>1

  • 长 >1 整型

  • 整型 >1 个字符

  • 整型 >1

  • 短 >1 字节

其中“S >1 T”表示“T是S的直接亚型”,根据本节正上方的JLS #4.10。

因此,在这种情况下,在没有直接匹配的情况下,并且在查看自动装箱之前,编译器会根据上述规则选择最接近的可用超类型,即。longfloat


答案 2

引用JLS的话

确定适用性的过程从确定可能适用的方法开始(§15.12.2.1)。

该过程的其余部分分为三个阶段,以确保与 Java SE 5.0 之前的 Java 编程语言版本的兼容性。这些阶段是:

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

这保证了在 Java SE 5.0 之前的 Java 编程语言中有效的任何调用都不会因为引入变量 arity 方法、隐式装箱和/或取消装箱而被视为模棱两可。但是,变量 arity 方法的声明 (§8.4.1) 可以更改为给定方法方法调用表达式选择的方法,因为变量 arity 方法在第一阶段被视为固定 arity 方法。例如,在已经声明 m(Object) 的类中声明 m(Object...) 会导致不再为某些调用表达式(如 m(null))选择 m(Object),因为 m(Object[]) 更具体。

  1. 第二阶段 (§15.12.2.3) 在允许装箱和取消装箱的同时执行重载解析,但仍排除使用可变 arity 方法调用。如果在此阶段找不到适用的方法,则处理将继续到第三阶段。...

编辑:关于选择浮点数而不是双精度:

如果多个成员方法既可访问又适用于方法调用,则需要选择一个成员方法来为运行时方法调度提供描述符。Java 编程语言使用选择最具体方法的规则。

非正式的直觉是,如果第一个方法处理的任何调用都可以在没有编译时错误的情况下传递给另一个方法,则一种方法比另一个方法更具体。

过载解析的第一阶段将选择这四种方法中的两种

private static void foo(double aDouble) 
private static void foo(float aFloat) 

因为第一阶段不允许装箱/取消装箱 (),并且如果没有显式转换,则无法传递给带有参数的方法。比最具体的方法会被选择。在这种情况下,方法将被解释为比 最具体。Longlongintfloatdouble