Java 中的方法重载解析

2022-09-02 11:45:00

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


编译器尝试从给定的重载方法定义解析方法调用的过程称为重载解析。如果编译器找不到完全匹配项,则它仅使用上行转换来查找最接近的匹配项(从不执行下行转换)。


下面是一个类:

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。

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

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会被打印出来呢?为什么编译器选择了将取为参数的方法,该参数是从 到 向下转换的?TestOverLoadintIntegerint


答案 1

编译器不会考虑向下转换,而是取消装箱转换以实现重载解析。在这里,将成功拆箱到一个。不考虑该方法,因为 无法将 a 加宽为 .唯一可能的过载是考虑取消装箱的重载,因此是打印的。IntegeriintStringIntegerString8

第一个代码输出的原因是编译器将考虑在拆箱转换上扩大引用转换(到)。10IntegerObject

JLS 的第 15.12.2 节在考虑哪些方法适用时指出:

  1. 第一阶段 (§15.12.2.2) 执行重载解析,不允许装箱或取消装箱转换,也不允许使用可变 arity 方法调用。如果在此阶段找不到适用的方法,则处理将继续到第二阶段。

 

  1. 第二阶段(§15.12.2.3)执行过载解析,同时允许装箱和拆箱[...]

答案 2

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

1. 加宽
2.自动装箱
3.Var-args

Java 编译器认为,加宽基元参数比执行自动装箱操作更可取。

换句话说,由于自动装箱是在Java 5中引入的,编译器在选择较新的样式(自动装箱)之前会选择较旧的样式(加宽),从而使现有代码更加健壮。var-args 也是如此。

在第一个代码片段中,会发生引用变量的加宽,即到而不是取消装箱,即到 。在您的第二个代码段中,从 到 因此不会发生加宽。IntegerObjectIntegerintIntegerString

考虑以下程序,它证明了上述所有陈述:

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

仅供参考,这是我关于Java中方法重载的博客

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