java中的重载方法优先级

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

研究这段代码:

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. 拳+可变参数

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

1.byte扩展到int

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

2.拳击

因此。 int – > IntegerInteger – > int arguments

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

 Integer int 

在输出中。

但我明白了

 int ... 

为什么?

现在很清楚,方法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转换为int ,然后对从intInteger的相同参数应用装箱转换,因为不允许按顺序进行两次转换。

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

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

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

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

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

所以..

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

Java只能做“盒子和宽”而不是“宽和盒子”。 例如,

  • int – > Number [OK!]
    • int boxed to Integer widen to Number
  • byte – > Integer [DOES NOT 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); } }