java中的方法重载解析

Posted

技术标签:

【中文标题】java中的方法重载解析【英文标题】:Method overload resolution in java 【发布时间】:2015-07-18 12:11:26 【问题描述】:

以下是我对 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 呢?为什么编译器选择了TestOverLoad 方法,它以int 作为参数,从Integer 向下转换为int

【问题讨论】:

Integer -> int 不是低调——它是拆箱,这是一个单独的机制。 因为足够接近将Integer 拆箱到int。 Java 从 Java 5 开始自动执行此操作。“自动装箱是 Java 编译器在基本类型及其相应的对象包装类之间进行的自动转换。例如,将 int 转换为 Integer,将 double 转换为 Double,以及以此类推。如果转换以其他方式进行,则称为拆箱。” 【参考方案1】:

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

第一个代码的输出是10 的原因是编译器会在取消装箱转换时考虑扩大引用转换(IntegerObject)。

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

    第一阶段(第 15.12.2.2 节)执行重载解决方案,不允许装箱或拆箱转换,或使用可变参数方法调用。如果在此阶段未找到适用的方法,则处理将继续到第二阶段。

 

    第二阶段 (§15.12.2.3) 执行重载解决方案,同时允许装箱和拆箱 [...]

【讨论】:

【参考方案2】:

在 Java 中,在方法重载的情况下解析方法按以下优先级完成:

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

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

换句话说,由于在 Java 5 中引入了 自动装箱,编译器会先选择较旧的样式(widening),然后再选择较新的样式(自动装箱),保持现有代码更健壮。 var-args 也是如此。

在您的第一个代码 sn-p 中,发生了引用变量的扩展,即 IntegerObject,而不是取消装箱,即 Integerint。在您的第二次 sn-p 中,不能从 Integer 扩大到 String,因此会发生拆箱。

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

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 int,int Long,Long

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

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

【讨论】:

【参考方案3】:

加宽击败拳击,拳击击败 var-args。在您的示例中,不会发生扩大,因此对其应用的装箱和 Integer 未装箱。没什么特别的。

【讨论】:

【参考方案4】:

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

1. 整数被解包/拆箱为基本类型int2. 然后调用TestOverLoad(int a) 方法。

在 main 方法中你声明 Integer 像 -

 Integer i = 9;  

然后调用 -

test.TestOverLoad(i);  

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

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

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:

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

一种适用的方法 m1 比另一种适用的方法更具体 方法 m2,用于使用参数表达式 e1、...、ek、if 的调用 以下任何一项为真: ...

m1 和 m2 可通过严格或松散调用适用,其中 m1 具有形参类型 S1、...、Sn 和 m2 具有形参 类型 T1, ..., Tn,类型 Si 比 Ti 更具体的参数 ei 代表所有 i (1 ≤ i ≤ n, n = k)。

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

【讨论】:

以上是关于java中的方法重载解析的主要内容,如果未能解决你的问题,请参考以下文章

Java重载和重写

关于Java中的方法重载

什么是Java 方法重载?

Java中的方法覆盖(Overriding)和方法重载(Overloading)是啥意思?

如何进行Java重载?Java编程学习

java重载和重写及类的加载顺序