java

时间:2015-05-07 18:44:04

标签: java overloading unboxing

以下是我对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的参数?

5 个答案:

答案 0 :(得分:19)

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

第一个代码输出为10的原因是编译器会考虑通过拆箱转换扩展引用转换(IntegerObject)。

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

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

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

答案 1 :(得分:13)

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

<强> 1。拓宽
 2.自动装箱
 3.变量

java编译器认为扩展原始参数比执行自动装箱操作更令人满意。

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

  

在您的第一个代码段中,扩展了引用变量,即IntegerObject而不是解除装箱,即Integerint。在第二个代码段中,从IntegerString不能进行扩展,因此会发生拆箱。

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

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中给出的一个例子的修改版本。

答案 2 :(得分:3)

拓宽节拍拳击,拳击节拍变速杆。在你的例子中,扩展不可能发生,所以应用的拳击和整数是未装箱的。没什么不寻常的。

答案 3 :(得分:2)

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

1。整数被解包/取消装箱到原始类型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的原因,然后调用第一个重载版本。

答案 4 :(得分:1)

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

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