为什么编译器更喜欢int过载到char的varargs char重载?

时间:2015-09-09 05:25:26

标签: java overloading

代码

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

4 个答案:

答案 0 :(得分:81)

当编译器确定要选择哪种重载方法时,使用varargs(...)的方法具有最低优先级。因此,当您使用单个TestOverload(int i)参数TestOverload(char... c)致电TestOverload时,会char选择'a',因为char可以自动提升为int TestOverload(char... c)

JLS 15.12.2

  
      
  1. 第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换,或使用变量arity   方法调用。如果在此阶段没有找到适用的方法   然后处理继续到第二阶段。   这保证了在Java编程中有效的任何调用   Java SE 5.0之前的语言不会被视为不明确的结果   引入变量arity方法,隐式装箱和/或   拆箱。但是,变量arity方法的声明(§8.4.1)   可以更改为给定方法方法调用选择的方法   表达式,因为变量arity方法被视为固定的   第一阶段的arity方法。例如,声明m(对象...)   在已经声明m(Object)的类中导致m(Object)为no   为某些调用表达式(例如m(null))选择更长的时间,如   m(Object [])更具体。

  2.   
  3. 第二阶段(§15.12.2.3)执行重载解析,同时允许装箱和拆箱,但仍然排除使用变量arity方法调用。如果在此期间未找到适用的方法   阶段然后处理继续到第三阶段。这确保了永远不会通过变量arity选择方法   方法调用,如果它适用于固定arity方法   调用

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

  6.   

编辑:

你希望强制编译器调用char[]构造函数,你可以传递给构造函数调用new TestOverload (new char[] {'a'});

{{1}}

答案 1 :(得分:36)

是的,这是预期的行为。方法调用的优先顺序如下:

  1. Widending
  2. 拳击
  3. 可变参数
  4. 以下是与Java docs相关的摘录: -

      

    确定适用性的过程首先要确定可能适用的方法(§15.12.2.1)。

         

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

         

    第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换,或使用变量arity方法调用。如果在此阶段没有找到适用的方法,则处理继续到第二阶段。

         

    这保证了在Java SE 5.0之前在Java编程语言中有效的任何调用都不会因为引入变量arity方法,隐式装箱和/或取消装箱而被认为是不明确的。但是,变量arity方法(第8.4.1节)的声明可以更改为给定方法方法调用表达式选择的方法,因为变量arity方法在第一阶段被视为固定arity方法。例如,在已声明m(Object)的类中声明m(Object ...)会导致不再为某些调用表达式(例如m(null))选择m(Object),因为m(Object [] )更具体。

         

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

         

    这确保了如果通过固定的arity方法调用适用,则永远不会通过变量arity方法调用选择方法。

         

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

答案 2 :(得分:13)

Joshua Bloch(Effective Java,2nd Ed)的坚实建议:

  

"只选择具有重复方法的参数作为具有-radically-different类型的参数。"

具有完全不同类型的对象是无法合理地转换为其他参数类型的对象。遵循这条规则可以节省数小时调试一个神秘的错误,当编译器在编译时选择你没想到的方法重载时会发生这种错误。

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

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

char可以与int互换,因此您可以预测调用将会发生什么的唯一方法是转到Java语言规范并阅读有关过载如何过度的一些神秘规则解决。

幸运的是,这种情况不应该需要JLS研究。如果你的参数彼此没有根本的不同,那么最好的选择是不要重载。为方法赋予不同的名称,以便任何可能需要维护代码的人都不会出错或混淆。

时间就是金钱。

答案 3 :(得分:0)

我从this link获取代码并修改了部分内容:

    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(为什么?)

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

+第一阶段:使用拓宽找到匹配方法(找不到匹配方法)

+第二阶段:(也)使用装箱/拆箱找到匹配方法(方法1,2和3匹配)

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