为什么编译器更喜欢一个int重载的可变char过载char?

public class TestOverload { public TestOverload(int i){System.out.println("Int");} public TestOverload(char... c){System.out.println("char");} public static void main(String[] args) { new TestOverload('a'); new TestOverload(65); } } 

产量

 Int Int 

这是预期的行为? 如果是的话,那为什么? 我期待:char,Int

注意:我正在使用Java 8

当编译器确定select哪种重载方法时,带有可变参数(varargs)的方法具有最低的优先级。 因此,当您使用单个char参数'a'调用TestOverload时, TestOverload(int i)是通过TestOverload(char... c)select'a' ,因为char可以自动提升为int

JLS 15.12.2 :

  1. 第一阶段(§15.12.2.2)执行重载parsing而不允许装箱或取消装箱转换,或者使用variables方法调用 。 如果在此阶段没有find适用的方法,则处理继续到第二阶段。 这保证了在Java SE 5.0之前的Java编程语言中有效的调用不会被认为是引入variablesarity方法,隐式装箱和/或取消装箱的结果。 但是,variablesarity方法(§8.4.1)的声明可以改变为给定方法方法调用expression式select的方法,因为variablesarity方法在第一个阶段被视为一个固定的arity方法。 例如,在已经声明m(Object)的类中声明m(Object …)将导致不再为某些调用expression式(如m(null))selectm(Object),如m(Object []) )更具体。

  2. 第二阶段(§15.12.2.3)在允许装箱和取消装箱的同时执行重载parsing,但是仍然排除使用variables方法调用 。 如果在此阶段没有find适用的方法,则处理继续到第三阶段。 这确保如果通过固定的arity方法调用适用方法,则通过variablesarity方法调用决不会select方法。

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

编辑:

你希望强制编译器调用TestOverload(char... c)构造函数,你可以传给构造函数调用一个char[]

 new TestOverload (new char[] {'a'}); 

是的,这是预期的行为。 方法调用的优先级是这样的:

  1. Widending
  2. 拳击
  3. 可变参数

以下是Java文档相关的摘录:

确定适用性的过程从确定可能适用的方法开始(第15.12.2.1节)。

该过程的其余部分分为三个阶段,以确保与Java SE 5.0之前的Java编程语言的版本兼容。 阶段是:

第一阶段(§15.12.2.2)执行重载parsing而不允许装箱或取消装箱转换,或者使用variables方法调用。 如果在此阶段没有find适用的方法,则处理继续到第二阶段。

这保证了在Java SE 5.0之前的Java编程语言中有效的调用不会被认为是引入variablesarity方法,隐式装箱和/或取消装箱的结果。 但是,variablesarity方法(§8.4.1)的声明可以改变为给定方法方法调用expression式select的方法,因为variablesarity方法在第一个阶段被视为一个固定的arity方法。 例如,在已经声明m(Object)的类中声明m(Object …)将导致不再为某些调用expression式(如m(null))selectm(Object),如m(Object []) )更具体。

第二阶段(§15.12.2.3)在允许装箱和拆箱的同时执行重载parsing,但是仍然排除了使用variables方法调用。 如果在此阶段没有find适用的方法,则处理继续到第三阶段。

这确保如果通过固定的arity方法调用适用方法,则通过variablesarity方法调用决不会select方法。

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

来自Joshua Bloch(Effective Java,第二版)的忠告:

“只select那些具有基本不同types的重载方法的参数。”

具有完全不同types的对象是不能合理地转换为另一个参数types的对象。 按照这个规则,可以节省数小时的时间来debugging一个神秘的错误,当编译器在编译时select你没有想到的方法重载时会发生一个神秘的错误。

您的代码行违反了这条规则,并为错误打开了大门:

 public TestOverload(int i){System.out.println("Int");} public TestOverload(char... c){System.out.println("char");} 

一个char可以与int ,所以预测调用会发生什么的唯一方法是转到Java语言规范,并阅读有关如何解决超载问题的有些神秘的规则。

幸运的是,这种情况不应该需要JLS研究。 如果你们的论据没有根本的不同,那么最好的办法就是不要超载 。 为这些方法提供不同的名称,以便任何可能需要维护代码的人都不可能出现错误或混淆。

时间就是金钱。

我从这个链接中获取了代码并修改了它的一些部分:

  public static void main(String[] args) { Byte i = 5; byte k = 5; aMethod(i, k); } //method 1 static void aMethod(byte i, Byte k) { System.out.println("Inside 1"); } //method 2 static void aMethod(byte i, int k) { System.out.println("Inside 2"); } //method 3 static void aMethod(Byte i, Byte k) { System.out.println("Inside 3 "); } //method 4 static void aMethod(Byte i, Byte ... k) { System.out.println("Inside 4 "); } 

编译器给方法1,2和3,但不是4(为什么?)错误(该方法是模糊的types重载)?

答案在于java用于匹配方法调用和方法签名的机制。 该机制分三个阶段完成,如果每个阶段发现匹配方法停止:

+第一阶段:使用扩大find匹配方法(找不到匹配的方法)

+第二阶段:(还)使用装箱/拆箱find匹配的方法(方法1,2和3匹配)

+第三阶段:(也)使用var args(方法4匹配!)