java 中的方法重载解析

Method overload resolution in java

以下是我对 java 中的重载解析的了解:


The process of compiler trying to resolve the method call from given overloaded method definitions is called overload resolution. If the compiler can not find the exact match it looks for the closest match by using upcasts only (downcasts are never done).


这是一个class:

public class MyTest {

    public static void main(String[] args) {
        MyTest test = new MyTest();
        Integer i = 9;
        test.TestOverLoad(i);
    }

    void TestOverLoad(int a){
        System.out.println(8);
    }

    void TestOverLoad(Object a){
        System.out.println(10);
    }

}

正如预期的那样,输出是 10。

但是,如果我稍微更改 class 定义并更改第二个重载方法。

public class MyTest {

    public static void main(String[] args) {
        MyTest test = new MyTest();
        Integer i = 9;
        test.TestOverLoad(i);
    }

    void TestOverLoad(int a){
        System.out.println(8);
    }

    void TestOverLoad(String a){
        System.out.println(10);
    }

}

输出为8。

在这里我很困惑。如果永远不会使用向下转换,那么为什么要打印 8 呢?为什么编译器选择 TestOverLoad 方法,该方法将 int 作为参数,它是从 Integerint 的向下转换?

实际上在第二个例子中并没有发生向下转型。发生了以下事情 -

1.整数是unwrapped/unboxed原始类型int.
2.然后调用TestOverLoad(int a)方法。

在 main 方法中你声明 Integer 就像 -

 Integer i = 9;  

然后调用 -

test.TestOverLoad(i);  

然而,您有 2 个重载版本的 TestOverLoad() -

TestOverLoad(int a); 
TestOverLoad(String a);

此处 TestOverLoad() 的第二个重载版本采用完全不同的参数 String。这就是为什么 Integer i 被拆箱为原始类型 int 并且之后调用第一个重载版本。

编译器不会考虑向下转换,而是考虑重载解析的拆箱转换。在这里,Integer i 将成功拆箱为 int。不考虑 String 方法,因为 Integer 无法扩展为 String。唯一可能的重载是考虑拆箱的重载,因此打印 8

第一个代码的输出是 10 的原因是编译器将考虑扩大参考转换(IntegerObject)而不是拆箱转换。

Section 15.12.2 of the JLS,在考虑哪些方法适用时,指出:

  1. The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

  1. The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing [...]

加宽胜过拳击,拳击胜过可变参数。在您的示例中,不会发生加宽,因此应用了装箱并且 Integer 被取消装箱。没什么特别的。

Java 中的所有对象都扩展了 class 对象,包括 class 整数。这两个class有如下关系: Integer "is a(n)" Object 因为Integer extends Object。在您的第一个示例中,使用了带有 Object 参数的方法。

在第二个示例中,没有找到接受整数的方法。在这种情况下 Java 使用所谓的自动拆箱将整数包装器 class 解析为原始 int。于是使用了int参数的方法

在 Java 中,在方法重载的情况下解析方法的优先级如下:

1.加宽
2. 自动装箱
3.可变参数

java 编译器认为扩大原始参数比执行自动装箱操作更可取。

换句话说,由于auto-boxing是在Java5中引入的,编译器选择了旧的风格(widening) 在它选择更新的样式(自动装箱)之前,保持现有代码更健壮。 var-args.

也一样

In your 1st code snippet, widening of reference variable occurs i.e, Integer to Object rather than un-boxing i.e, Integer to int. And in your 2nd snippet, widening cannot happen from Integer to String so unboxing happens.

考虑以下证明上述所有陈述的程序:

class MethodOverloading {

    static void go(Long x) {
        System.out.print("Long ");
    }

    static void go(double x) {
        System.out.print("double ");
    }

    static void go(Double x) {
        System.out.print("Double ");
    }

    static void go(int x, int y) {
        System.out.print("int,int ");
    }

    static void go(byte... x) {
        System.out.print("byte... ");
    }

    static void go(Long x, Long y) {
        System.out.print("Long,Long ");
    }

    static void go(long... x) {
        System.out.print("long... ");
    }

    public static void main(String[] args) {
        byte b = 5;
        short s = 5;
        long l = 5;
        float f = 5.0f;
        // widening beats autoboxing
        go(b);
        go(s);
        go(l);
        go(f);
        // widening beats var-args
        go(b, b);
        // auto-boxing beats var-args
        go(l, l);
    }
}

输出为:

double double double double int,int Long,Long

仅供参考,这是我的blog on method overloading in Java

P.S: 我的答案是SCJP中给出的例子的修改版本。

虽然@rgettman 接受的答案导致非常正确的来源,更准确地说,JLS Section 15.12.2 的 §15.12.2.2 和 §15.12.2.3 讨论了适用性,不是 resolution - OP 要求的。在示例中,OP 提供的两种 testOverLoad 方法都是 applicable,即将在没有另一个的情况下成功解决。 相反,15.12.2.5. Choosing the Most Specific Method 讨论了适用方法的 分辨率 。 上面写着:

One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true: ...

m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).

因此,在 OP 提供的第一个示例中,Integer 类型的参数 i 方法 testOverLoad(Object a) 更具体 testOverLoad(int a).