我知道这个问题讨论过很多次,但我还是不明白。
研究这段代码:
public class Main {
public static void var(Integer x, int y) {
System.out.println("Integer int");
}
public static void var(int... x) {
System.out.println("int... x");
}
public static void var(Integer... x) {
System.out.println("Integer...");
}
public static void main(String... args) {
byte i = 0;
Integer i2 = 127;
var(i, i2);
}
}
在我的大脑中遵循以下规则:
加宽
拳击
装箱+可变参数
根据这个规则我做下一步的 Action
1.byte 宽度为 int
现在我有 int
Integer
并且存在方法采用 Integer
和 int
2.打拳
因此。 int
-> Integer
和 Integer
-> int
参数
我认为论点是适用的,期待看到
Integer int
在输出中。
但是我明白了
int ...
为什么?
最佳答案
现在很清楚方法var(int...)
被选中而不是 var(Integer...)
.
原因是只允许应用某些转换,并且只能是列表中的这些转换之一,而不是转换链。 java 编译器不允许先进行扩大原始类型转换,然后再进行装箱转换。
它在 Java Language Specification in section 5.3 中指定
5.3. Method Invocation Conversion
Method invocation conversion is applied to each argument value in a method or constructor invocation (§8.8.7.1, §15.9, §15.12): the type of the argument expression must be converted to the type of the corresponding parameter.
Method invocation contexts allow the use of _one of_ the following:
- an identity conversion (§5.1.1)
- a widening primitive conversion (§5.1.2)
- a widening reference conversion (§5.1.5)
- a boxing conversion (§5.1.7) optionally followed by widening reference conversion
- an unboxing conversion (§5.1.8) optionally followed by a widening primitive conversion.
编译器唯一的选择是:
- 第一个参数的扩展原始转换
- 第二个参数的拆箱转换
那变成了(byte, Integer)
进入(int, int)
.
它不能首先转动第一个参数 byte
进入 int
然后对来自 int
的相同参数应用装箱转换至 Integer
因为不允许依次进行两次转换。
让我们回头看看编译器是如何选择调用哪个重载方法的。这在 JLS 15.12.2 中有描述。 . (15.12.1描述了如何查找要搜索的类或接口(interface),但是我们已经知道我们要调用类中的静态方法Main
)
编译器为选择正确的重载方法而经历的前两个阶段不适用于可变参数(“可变元数”)方法,但第三阶段适用:
The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.
第 15.12.4 节相当复杂,但这里适用的规则是:
- 首先应用非变量参数的规则(不适用于您的情况)
- 调用中的每个变量参数必须可以通过方法调用转换(我在上面复制的部分)转换为变量参数声明的类型
所以..
- 您尝试调用名为
var
的方法与(byte, Integer)
- 编译器查看你的方法
var(Integer...)
- 它问:我可以转换第一个参数吗,一个
byte
, 至Integer
(方法中参数的声明类型) - 它查看 JLS 5.3 中的规则。它只能应用 5 个列表中的一个转换。它们都不能转换
byte
到Integer
直接 - 它不能执行两个步骤。 - 所以编译器决定它不能选择
var(Integer...)
- 然后它会查看您的其他方法,
var(int...)
- 根据 JLS 5.3,它可以转换您的第一个参数,
byte
到int
使用扩大原始转换。这是一个复选标记。 - 继续第二个参数,你的
Integer
,它看到 JLS 5.3 允许编译器将其转换为int
使用拆箱转换。所以这也是一个复选标记。 - 这是最后一个参数,所以
var(int...)
是一个很好的匹配。 - 编译器现在继续查看是否有更多方法与您的调用相匹配。如果有更多的人这样做,那将导致模棱两可的调用错误。
- 但是没有更多方法名为
var
, 所以var(int...)
是唯一适用的方法。编译器现在将生成代码以进行必要的转换并调用该方法。
关于java - java中的重载方法优先级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22590914/