Rom*_*man 31 java overloading type-conversion
这是一个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
}
}
Run Code Online (Sandbox Code Playgroud)
为什么java会这样做?
Tom*_*icz 31
Java语言规范非常明确(强调我的):
15.12.2编译时步骤2:确定方法签名
[...]
第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换[...]如果在此阶段没有找到适用的方法,则处理继续到第二阶段.[...]
第二阶段(§15.12.2.3)执行重载解析,同时允许装箱和拆箱 [...]
第三阶段(§15.12.2.4)允许重载与变量arity方法,装箱和拆箱相结合.
也就是说,仅在第一步中print(int)并且print(float)可以是适当的.后者匹配,没有进一步调查.
这些规则的原因也在JLS中解释:
这保证了在Java SE 5.0之前在Java编程语言中有效的任何调用都不会因为引入变量arity方法,隐式装箱和/或取消装箱而被认为是不明确的.
想象一下,您的Test类是针对Java 1.4编译的(在自动装箱之前).在这种情况下,它很清楚:print(float)必须选择(假设我们同意,为什么long要float被认为是安全的,可以是隐式的...),因为print(Long)是完全不兼容long的论点.
稍后您将针对Java 5+编译相同的代码.编译器可以:
在这种情况下选择print(Long)更" 明显 ".因此在升级到Java 5后,您的程序行为有所不同......
由于调用不明确,会产生编译错误.因此,以前正确的代码不再在Java 5下编译(AFAIR永远不会这样)
...或保留旧语义并调用与Java 1.4相同的方法
您现在应该了解使用的原因print(float)- 因为它将在Java 1.4下选择.Java必须向后兼容.
Tomasz Nurkiewicz指出了规范的相关部分(Java SE 7 JLS中的15.12.2),但为什么这样做呢?对于针对1.4及更早版本的向后兼容性源代码,应继续调用相同的重载方法.因此,必须忽略1.5的特性,并且只有在代码不能编译的情况下才应考虑自动装箱.
至于为何从转换long到float可能是隐含的-这只是一个可疑的设计选择.
查看文档
5.1.2. 扩大原始转换
原始类型的 19 种特定转换称为扩展原始类型转换:
- 字节到 short、int、long、float 或 double
- 短到 int、long、float 或 double
- char 到 int、long、float 或 double
- int 到 long、float 或 double
- long 浮动或加倍
- 浮动到两倍
所以转换形式long到float是有规则的。