在java中重载方法优先级

gst*_*low 6 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);  
    }  
} 
Run Code Online (Sandbox Code Playgroud)

在我的大脑遵循规则:

  1. 加宽

  2. 拳击

  3. 拳+可变参数

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

1.byte扩展到int

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

2.make拳击

因此.int- > IntegerInteger- > int参数

我认为这些论点是适用的,并且有望看到

Integer int
Run Code Online (Sandbox Code Playgroud)

在输出中.

但我明白了

int ...
Run Code Online (Sandbox Code Playgroud)

为什么?

Erw*_*idt 5

现在很清楚,该方法var(int...)是选择的而不是var(Integer...).

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

在第5.3节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节),可选地后跟一个加宽的基元转换.

编译器的唯一选择是:

  1. 第一个参数的扩展原始转换
  2. 第二个参数的拆箱转换

那变成(byte, Integer)(int, int).

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

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

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

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

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

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

所以..

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


hum*_*ngV 5

Java只能做到“框加宽”,不能做到“宽加框”。例如,

  • int --> 数字 [确定!]
    • int 装箱为整数 加宽为数字
  • 字节 --> 整数 [不编译]
    • byte 需要先加宽为 int,然后装箱为 Integer。Java 不允许。请注意,您不能将这个 -byte 框转换为 Byte ,然后扩大为 Integer(Integer 不是 Byte 的超类)。

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

我编写了以下演示类:

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

    }

}
Run Code Online (Sandbox Code Playgroud)