JVM如何在函数重载的情况下找到要调用的方法(具有最接近匹配的参数)
JVM决定在编译时调用哪个重载方法。 我有一个例子:
public class MainClass{ public static void go(Long n) {System.out.println("takes Long ");} public static void go(Short n) {System.out.println("takes Short ");} public static void go(int n) {System.out.println("takes int ");} public static void main(String [] args) { short y = 6; long z = 7; go(y); go(z); go((Short)y); } }
根据我的理解,它应该打印以下内容:
takes Short takes Long takes Short
…但实际输出是:
takes int takes Long takes Short
但是,如果我有以下三个function:
public static void go(Integer n) {System.out.println("takes Integer");} public static void go(Long n) {System.out.println("takes Long ");} public static void go(Short n) {System.out.println("takes Short ");}
…并使用以下方式调用它:
int a= 10; and go(i); //output : takes Integer.
…为什么short
和int
存在差异?
有关规则,请参阅JLS第15.12.2节 ,以确定要调用的方法。 如果方法重载,编译器总是选择最具体的方法:
可能存在多于一种这样的方法,在这种情况下,选择最具体的方法。 最具体方法的描述符(签名加返回类型)是在运行时用于执行方法分派的方法。
编译器首先尝试在没有装箱或拆箱的情况下解决方法,如下所示:
第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换 ,或使用变量arity方法调用。 如果在此阶段没有找到适用的方法,则处理继续到第二阶段。
强调我的。
所以,在你的第一个代码中, short可以用作int类型参数的参数。 编译器不会使用参数Short
的方法,因为这需要装箱。 虽然在long类型的情况下,因为它不能用作int类型的参数,所以它将它装入Long
。 记住拓宽比拳击更受欢迎 。
在你的第二个中 ,除了将int
装入int
之外别无他法。 因此,它使用Integer
参数调用方法。
JVM根本找不到它。 编译器。 它根据JLS第15.12.2.5节中的规则选择最具体的方法:
如果多个成员方法都可访问并适用于方法调用,则必须选择一个为运行时方法调度提供描述符。 Java编程语言使用选择最具体方法的规则。
非正式的直觉是,如果第一个方法处理的任何调用都可以传递给另一个没有编译时类型错误的调用,那么一个方法比另一个方法更具体。
……(完整规则)……
因为upcasting to int是在Java 1.0版本中,并且在5.0版本中添加了自动装箱。 更改行为会破坏为旧版Java编写的代码。
在拳击之前发生加宽(如果有的话)。 如此short
将成为int
并调用该方法。
此外,与这个问题没有直接关系但有趣的一点:你不能盒子和加宽即short
不能成为Integer
Java首先查找最接近的匹配项。 它试图找到以下内容:
- 按类型完全匹配
- 匹配超类类型
- 转换为更大的基本类型
- 转换为自动盒装类型
- 可变参数
下面的video清楚地解释了JVM在方法重载的情况下如何在各种合格方法中选择一种方法的方式。