这是一个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会这样做?
答案 0 :(得分:31)
Java Language Specification非常清楚(强调我的):
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必须向后兼容。
答案 1 :(得分:12)
选择float
超过Long
的原因是后来添加了自动装箱,出于向后兼容的原因,它必须始终进行相同的调用。
答案 2 :(得分:7)
Tomasz Nurkiewicz指出规范的相关部分(15.12.2 in Java SE 7 JLS),但为什么这样做?对于针对1.4及更早版本的向后兼容性源代码,应继续调用相同的重载方法。因此,必须忽略1.5的特性,并且只有在代码无法编译时才应考虑自动装箱。
至于为什么从long
到float
的转换可能是隐含的 - 这只是一个值得怀疑的设计选择。
答案 3 :(得分:4)
参见文档
Chapter 5. Conversions and Promotions
5.1.2。扩大原始转换
对原始类型的19个特定转换称为扩展 原始转换:
- byte to short,int,long,float或double
- 简称为int,long,float或double
- char to int,long,float或double
- int to long,float或double
- 长期漂浮或加倍
- 浮动加倍
因此,long
到float
的转换形式与规则一致。