java中的重载方法优先级
我知道这个问题多次讨论过,但我还是不明白。
研究这段代码:
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); } }
在我的大脑遵循规则:
-
加宽
-
拳击
-
拳+可变参数
根据这条规则,我做了下一步行动
1.byte扩展到int
现在我有int
Integer
并且存在方法需要Integer
和int
2.拳击
因此。 int
– > Integer
和Integer
– > int
arguments
我认为这些论点是适用的,并且有望看到
Integer int
在输出中。
但我明白了
int ...
为什么?
现在很清楚,方法var(int...)
被选中而不是var(Integer...)
。
原因是只允许应用某些转换,并且它只能是列表中的其中一个转换,而不是转换链。 首先不允许java编译器进行扩展原语转换,然后进行装箱转换。
它在第5.3节的Java语言规范中指定
5.3。 方法调用转换
方法调用转换应用于方法或构造函数调用中的每个参数值(第8.8.7.1节,第15.9节,第15.12节):必须将参数表达式的类型转换为相应参数的类型。
方法调用上下文允许使用_以下_ 之一 :
- 身份转换(第5.1.1节)
- 扩展的原始转换(第5.1.2节)
- 扩大参考转换(第5.1.5节)
- 一个拳击转换(§5.1.7),随后可以加宽引用转换
- 一个拆箱转换(第5.1.8节),可选地后跟一个加宽的基元转换。
编译器的唯一选择是:
- 第一个参数的扩展原始转换
- 第二个参数的拆箱转换
这会将(byte, Integer)
转换为(int, int)
。
它不能先将第一个参数byte
转换为int
,然后对从int
到Integer
的相同参数应用装箱转换,因为不允许按顺序进行两次转换。
让我们回过头来了解编译器如何选择要调用的重载方法。 这在JLS 15.12.2中有所描述。 (15.12.1描述了如何查找要搜索的类或接口,但我们已经知道我们要在Main
类中调用静态方法)
编译器选择正确重载方法的前两个阶段不适用于变量参数(“变量arity”)方法,但第三阶段确实如此:
第三阶段(§15.12.2.4)允许重载与变量arity方法,装箱和拆箱相结合。
第15.12.4节非常复杂,但适用的规则是:
- 首先应用非变量arity参数的规则(不适用于您的情况)
- 调用中的每个变量参数必须可以通过方法调用转换 (我上面复制的那个)转换为变量参数声明的类型
所以..
- 您尝试使用
(byte, Integer)
调用名为var
的方法 - 编译器查看你的方法
var(Integer...)
- 它问:我可以将第一个参数(一个
byte
转换为Integer
(方法中声明的参数类型) - 它着眼于JLS 5.3中的规则。 它只能应用列表中的一个转换。它们都不能直接将
byte
转换为Integer
– 它不能执行两个步骤。 - 所以编译器决定它不能选择
var(Integer...)
- 然后它会查看你的另一个方法
var(int...)
- 根据JLS 5.3,它可以使用扩展的原始转换将您的第一个参数,
byte
转换为int
。 这是一个复选标记。 - 转到第二个参数,即
Integer
,它看到JLS 5.3允许编译器使用拆箱转换将其转换为int
。 所以这也是一个复选标记。 - 那是最后一个参数,所以
var(int...)
是一个很好的匹配。 - 编译器现在继续查看是否有更多方法与您的调用匹配。 如果还有更多,那将导致模糊调用错误。
- 但是没有更多名称为
var
方法,因此var(int...)
是唯一适用的方法。 编译器现在将生成代码以执行必要的转换并调用该方法。
Java只能做“盒子和宽”而不是“宽和盒子”。 例如,
- int – > Number [OK!]
- int boxed to Integer widen to Number
- byte – > Integer [DOES NOT COMPILE]
- byte需要先扩展为int,然后将box扩展为Integer。 Java不允许它。 请注意,您不能将此字节框转换为Byte, 然后扩展为Integer (整数不是字节的超类)。
因此,在您给定的方法中,第一个参数字节已经失败了两个Integer方法。 因此,只有int …适用。
我为demo编写了以下类:
public class Overload{ public static void primitiveWiden(int x){ System.out.println("int"); } public static void refWiden(Map m){ System.out.println("Map"); } public static void priWideAndBox(Integer o){//doesn't work System.out.println("Object"); } public static void boxAndRefWide(Number n){//it works System.out.println("Number"); } public static void main(String[] args){ byte b =0; int i =0; HashMap m = new HashMap(); primitiveWiden(b); refWiden(m); priWideAndBox(b);//compile error boxAndRefWide(i); } }