在java中重载方法优先级

时间:2014-03-23 12:29:17

标签: java overloading

我知道这个问题多次讨论过,但我还是不明白。

研究此代码:

public class Main {  
    public static void var(Integer x, int y) {  
        System.out.println("Integer int");  
    }  


    public static void var(int... x) {  
        System.out.println("int... x");  
    }  

    public static void var(Integer... x) {  
        System.out.println("Integer...");  
    }  

    public static void main(String... args) {   
        byte i = 0;  
        Integer i2 = 127;  
        var(i, i2);  
    }  
} 

在我的大脑遵循规则:

  1. 加宽

  2. 拳击

  3. 拳击+可变参数

  4. 根据这条规则我会做下一步行动

    1.byte wides to int

    现在我有int Integer,并且存在方法需要Integerint

    2.make boxing

    因此。 int - > IntegerInteger - > int个参数

    我认为这些论点是适用的,并且预计会看到

    Integer int
    
    输出中的

    但我明白了

    int ...
    

    为什么?

2 个答案:

答案 0 :(得分:5)

现在很明显,方法var(int...)已被选中,而不是var(Integer...)

原因是只允许应用某些转化,并且它只能是列表中的转化之一,而不是转化链。 不允许java编译器首先进行扩展原语转换,然后进行装箱转换。

Java Language Specification in section 5.3

中指定
  

5.3。方法调用转换

     

方法调用转换应用于a中的每个参数值   方法或构造函数调用(§8.8.7.1,§15.9,§15.12):类型   必须将参数表达式转换为类型   相应的参数。

     

方法调用上下文允许使用_ _中的一个

     
      
  • 身份转换(第5.1.1节)
  •   
  • 扩大原始转换(第5.1.2节)
  •   
  • 扩大参考转换(第5.1.5节)
  •   
  • 拳击转换(§5.1.7),可选地后跟加宽引用   转化
  •   
  • 取消装箱转换(第5.1.8节),可选地后跟加宽   原始转换。
  •   

编译器的唯一选择是:

  1. 第一个参数的扩展原始转换
  2. 第二个参数的拆箱转换
  3. (byte, Integer)变为(int, int)

    首先不能将第一个参数byte转换为int,然后对intInteger的相同参数应用装箱转换,因为顺序中的两个转换是不允许。

    让我们回过头来了解编译器如何选择要调用的重载方法。这在JLS 15.12.2中有所描述。 (15.12.1描述了如何找到要搜索的类或接口,但我们已经知道我们想在类Main中调用静态方法)

    编译器选择正确重载方法的前两个阶段不适用于变量参数(“变量arity”)方法,但第三阶段确实如此:

      

    第三阶段(§15.12.2.4)允许重载与   变量arity方法,装箱和拆箱。

    第15.12.4节非常复杂,但适用的规则是:

    • 首先应用非变量arity参数的规则(在您的情况下不适用)
    • 调用中的每个变量参数必须可以通过方法调用转换(我上面复制的那个)转换为变量参数声明的类型

    所以......

    1. 您尝试使用var
    2. 调用名为(byte, Integer)的方法
    3. 编译器会查看您的方法var(Integer...)
    4. 它问:我可以将第一个参数byte转换为Integer(方法中声明的参数类型)
    5. 它着眼于JLS 5.3中的规则。它只能应用5个列表中的一个转化。它们都不能直接将byte转换为Integer - 它不能执行两个步骤。
    6. 因此编译器决定不能选择var(Integer...)
    7. 然后它会查看您的其他方法var(int...)
    8. 根据JLS 5.3,它可以使用扩展原语转换将您的第一个参数byte转换为int。这是一个复选标记。
    9. 继续讨论第二个参数Integer,它看到JLS 5.3允许编译器使用拆箱转换将其转换为int。所以这也是一个复选标记。
    10. 那是最后一个参数,所以var(int...)是一个很好的匹配。
    11. 编译器现在继续查看是否有更多方法与您的调用匹配。如果还有更多,那将导致模糊调用错误。
    12. 但是没有更多名称为var的方法,因此var(int...)是唯一适用的方法。编译器现在将生成代码以执行必要的转换并调用该方法。

答案 1 :(得分:2)

Java只能做" box和wide"不是"宽和盒子"。例如,

  • int - >数字[OK!]
    • int boxed to Integer widen to Number
  • byte - >整数[DOESN' T COMPILE]
    • byte需要先扩展为int,然后将box扩展为Integer。 Java没有 允许它。请注意,您不能执行Byte 的字节框 扩展为Integer (整数不是字节的超类)。

因此,在您给定的方法中,第一个参数字节已经失败了两个Integer方法。因此,只有int ...适用。

我为demo编写了以下类:

public class Overload{

    public static void primitiveWiden(int x){
        System.out.println("int");
    }
    public static void refWiden(Map m){
        System.out.println("Map");
    }
    public static void priWideAndBox(Integer o){//doesn't work
        System.out.println("Object");
    }
    public static void boxAndRefWide(Number n){//it works
        System.out.println("Number");
    }

    public static void main(String[] args){
        byte b =0;
        int i =0;
        HashMap m = new HashMap();

        primitiveWiden(b);
        refWiden(m);
        priWideAndBox(b);//compile error
        boxAndRefWide(i);

    }

}