我试图了解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");
}
Run Code Online (Sandbox Code Playgroud)
为什么呼叫会解决foo(float aFloat).我从JLS了解以下内容,
此步骤使用方法的名称和参数表达式的类型来定位可访问和适用的方法可能有多个此类方法,在这种情况下,选择最具体的方法.
我故意在这里使用Wrapper Long而不是原始的long.长度为64位的原始长度不会结束foo(double aDouble)但是32位浮点数foo(float aFloat).
问题为什么Java隐式(没有强制转换)将`long`转换为`float`?进一步澄清了这个问题的答案.
use*_*421 15
这是因为JLS#15中的"最具体"规则又引用了JLS#4.10,后者又引用了#4.10.1,其中指出:
以下规则定义了基本类型之间的直接超类型关系:
double> 1 float
漂浮> 1长
长> 1 int
int> 1个字符
int> 1短
短> 1个字节
其中"S> 1 T"表示"T是S的直接子类型",根据本节紧接的JLS#4.10.
所以在这种情况下,如果没有直接匹配long,并且在查看自动装箱之前,编译器会选择最接近的可用超类型,即float上述规则.
来自JLS的报价:
确定适用性的过程首先确定可能适用的方法(§15.12.2.1).
该过程的其余部分分为三个阶段,以确保与Java SE 5.0之前的Java编程语言版本兼容.阶段是:
- 第一阶段(§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 [] )更具体.
- 第二阶段(§15.12.2.3)执行重载解析,同时允许装箱和拆箱,但仍然排除使用变量arity方法调用.如果在此阶段没有找到适用的方法,则处理继续到第三阶段....
编辑:关于选择 float而不是double:
如果多个成员方法都可访问并适用于方法调用,则必须选择一个为运行时方法调度提供描述符.Java编程语言使用选择最具体方法的规则.
非正式的直觉是,如果第一个方法处理的任何调用都可以传递给另一个没有编译时错误的调用,那么一个方法比另一个方法更具体.
重载决策的第一阶段将选择这四种方法中的两种
private static void foo(double aDouble)
private static void foo(float aFloat)
Run Code Online (Sandbox Code Playgroud)
因为第一阶段不允许装箱/取消装箱(Long),你不能long在int没有显式投射的情况下传递给带参数的方法.将选择最具体的方法.在这种情况下,float方法将被解释为最具体的double.