22

我刚刚发现了一个非常有趣的 Java 技巧:

void method1(Integer... a){
}

所以你可以给这个方法任意数量的整数。

现在,如果我有类似的(重载)方法,如下所示:

void method1(int a, int b){

}

当我执行以下行时运行哪个方法:

method1(1, 2);

好吧,我可以很容易地通过使用不同的方法指令对其进行测试来发现这一点,但是当我考虑“重载”方法中的“规则”时,我必须确保每个重载的方法必须相同,以便编译器知道究竟要使用哪一个。

在我看来,上面的代码不应该工作,因为编译器应该被混淆。但是当我尝试它时,它可以工作。

那么..有人知道更多关于此的背景信息吗?

4

5 回答 5

30

为了确定应该调用哪个方法,编译器会检查以下列表,如JLS #5.3JLS #15.12.2中所述:

  • 身份转换(§5.1.1)=>method1(int a, int b)
  • 扩大的原始转换(§5.1.2)
  • 扩大参考转换(§5.1.5)
  • 拳击转换(第 5.1.7 节)可选地后跟扩大参考转换 ==>method1(Integer... a)
  • 一个拆箱转换(第 5.1.8 节)可选地后跟一个扩大的原始转换。

在您的情况下,第一点适用并被method1(int, int)调用。

(更准确地说,您的方法使用 varags 并且优先级低于简单的装箱转换。换句话说,如果有 amethod1(Integer a, Integer b)它会method1(Integer... a)在层次结构中出现)

为什么会这样?15.12.2 中的评论给出了提示:

这保证了在 Java SE 5.0 之前在 Java 编程语言中有效的任何调用都不会因为引入可变参数方法、隐式装箱和/或拆箱而被认为是模棱两可的。

于 2012-08-03T21:33:34.657 回答
4

第二种方法获胜。根据Java 语言规范 (pdf)

第一阶段(第 15.12.2.2 节)执行重载决议,不允许装箱或拆箱转换,或使用变量 arity 方法调用。

如果在此阶段找到适用的方法,则该方法获胜;不再进行搜索。在您的情况下,它恰好是第二种方法,因为第一种方法是一种变量 arity 方法,也需要装箱。

于 2012-08-03T21:35:59.180 回答
2

我猜了一半,但就像 thinksteep 所说,Integer...实际上被视为一个Integer数组,这意味着您的方法调用必须执行两次强制以使其与第一个方法匹配(将ints 装箱为Integers,并将您的参数列表视为数组而不是简单的两个不同的参数)。然而,不需要强制来使其与第二种方法相匹配。

好的,我可以看到其他几个人已经引用了比我提供的更具体的 JLS,而我正在输入这个。

于 2012-08-03T21:40:51.973 回答
2

将对 method1(int a, int b) 进行调用。刚刚检查了这个字节码 - 这里Integer... a实际上是Integer[] a 一个详细的转换检查这些分配转换

于 2012-08-03T21:39:46.420 回答
1

可变参数的优先级最低。如果没有找到其他匹配的方法,则只调用它。这就像SWITCH案例的默认案例。

于 2016-05-27T04:21:14.023 回答