java中重载方法优先级

java中重载方法优先级,java,overloading,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 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);  
    }  
} 
在我的大脑中,有以下规则:

  • 加宽

  • 拳击

  • 拳击+varargs

  • 根据这条规则,我将采取下一步行动

    1.1字节宽到整数

    现在我有了
    int
    Integer
    ,还有exist方法takes
    Integer
    int

    打拳击

    因此
    int
    ->
    Integer
    Integer
    ->
    int
    参数

    我认为这些论据是适用的,并期望看到

    Integer int
    
    在输出方面

    但我明白了

    int ...
    

    为什么?

    现在很清楚,选择了方法
    var(int…)
    ,而不是
    var(Integer…

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

    这是合同中规定的

    5.3。方法调用转换

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

    方法调用上下文允许使用以下内容之一:

    • 身份转换(§5.1.1)
    • 扩大原语转换(§5.1.2)
    • 拓宽参考转换(§5.1.5)
    • 装箱转换(§5.1.7)后可选加宽参考 转化
    • 拆箱转换(§5.1.8)后可选加宽 原语转换
    编译器的唯一选项是:

  • 在第一个参数上进行扩大的原语转换
  • 关于第二个参数的取消装箱转换
  • 这将
    (字节,整数)
    转换为
    (int,int)

    它不能先将第一个参数
    byte
    转换为
    int
    ,然后对同一个参数应用从
    int
    Integer
    的装箱转换,因为不允许按顺序进行两次转换

    让我们回到前一步,了解编译器如何选择要调用的重载方法。如中所述。(15.12.1描述了如何找到要搜索的类或接口,但我们已经知道我们想要调用class
    Main
    中的静态方法)

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

    第三阶段(§15.12.2.4)允许超载与 变量算术方法、装箱和取消装箱

    第15.12.4节相当复杂,但此处适用的规则如下:

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

  • 您尝试使用
    (字节,整数)
    调用名为
    var
    的方法
  • 编译器查看您的方法
    var(Integer…
  • 它询问:我可以将第一个参数
    字节
    转换为
    整数
    (方法中声明的参数类型)吗
  • 它查看JLS 5.3中的规则。它只能应用5个列表中的一个转换。它们都不能将
    字节
    直接转换为
    整数
    ——它不能执行两个步骤
  • 因此编译器决定它不能选择
    var(Integer…
  • 然后看一下您的另一个方法,
    var(int…)
  • 根据JLS 5.3,它可以使用扩展原语转换将第一个参数
    字节
    转换为
    int
    。这是一个复选标记
  • 转到第二个参数,
    整数
    ,它看到JLS 5.3允许编译器使用拆箱转换将其转换为
    int
    。所以这也是一个复选标记
  • 这是最后一个参数,因此
    var(int…)
    是一个很好的匹配
  • 编译器现在继续查看是否有更多的方法与您的调用相匹配。如果有更多这样做,那将导致一个不明确的调用错误
  • 但是没有更多名为
    var
    的方法,因此
    var(int…)
    是唯一适用的方法。编译器现在将生成代码来进行必要的转换并调用该方法
  • Java只能做“框和宽”而不能做“宽和宽”。比如说,

    • 整数-->数字[确定!]
      • 整型装箱到整型加宽到数字
    • 字节-->整数[不编译]
      • 字节需要先加宽到int,然后长方体加宽到整数。Java没有 允许吧。请注意,你不能这样做-字节框到字节然后 扩展为整数(整数不是字节的超类)
    因此,在给定的方法中,第一个参数字节已经使两个整数方法失败。所以,只有在。。。这是适用的

    我为演示编写了以下类:

    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);
    
        }
    
    }
    

    我希望看到这段代码没有编译,看起来我的期望是正确的:@OliCharlesworth可能是JDK问题?@OliCharlesworth我得到了相同的错误。@gstackoverflow。。你在哪个java版本上编译?@Rohit Jain抱歉。我更新了主题。这对我来说已经够复杂的解释了。你能解释一下描述简单的动作吗?如何思考编译器编译器如何解决重载问题吗?好的,我会补充一些关于它如何选择调用哪个方法的信息,有时候事情并不是那么清楚