为什么编译器更喜欢 int 重载而不是 varargs char 重载?

2022-08-31 15:36:19

法典

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

这是预期的行为吗?如果是这样,那么为什么?我期待:字符,整数

注意:我使用的是 Java 8


答案 1

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

JLS 15.12.2

  1. 第一阶段 (§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[]) 更具体。

  2. 第二阶段 (§15.12.2.3) 在允许装箱和取消装箱的同时执行重载解析,但仍排除使用可变 arity 方法调用。如果在此阶段找不到适用的方法,则处理将继续到第三阶段。这可确保如果某个方法通过固定 arity 方法调用适用,则该方法永远不会通过可变 arity 方法调用来选择。

  3. 第三阶段 (§15.12.2.4) 允许将重载与可变 arity 方法、装箱和取消装箱相结合。

编辑:

如果您希望强制编译器调用构造函数,则可以传递给构造函数调用:TestOverload(char... c)char[]

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

答案 2

是的,这是预期的行为。方法调用的优先级如下所示:

  1. 加宽
  2. 拳击
  3. 瓦拉格斯

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

确定适用性的过程从确定可能适用的方法开始(§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 方法、装箱和取消装箱相结合。