【问题标题】:Which method is called? (Integer... a) vs. (int a, int b)调用哪种方法? (整数... a) 与 (int a, int b)
【发布时间】:2012-08-01 23:08:48
【问题描述】:

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

void method1(Integer... a){
}

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

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

void method1(int a, int b){

}

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

method1(1, 2);

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

在我看来,上面的代码不应该工作,因为编译器应该是混乱的。但是当我尝试它时,它会起作用。

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

【问题讨论】:

  • 这不是(该问题的)完全相同的副本。另一个问题有long... 重载(可变参数和扩展),这有int,int。所以,虽然它是相关的 ..
  • stackoverflow.com/questions/4921589/…(获取“相关”)
  • 现在,如果它被调用为:method(new Integer(1), new Integer(2)) ..?当呼叫走这条路线时,看到它被解决会很有趣。我不太了解规则:-/
  • @pst 拆箱在层次结构中的可变参数之前进行,因此仍会调用 method1(int, int)
  • 不是重复的。投票错误地关闭。

标签: java methods overloading variadic-functions


【解决方案1】:

为了确定应该调用哪个方法,编译器会检查以下列表,详见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,其优先级低于简单的装箱转换。换句话说,如果有 method1(Integer a, Integer b),它将在层次结构中位于 method1(Integer... a) 之前)

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

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

【讨论】:

  • 好吧,看来我在学校学到的重载方法的基本规则背后其实还有更多。所以你说java编译器遍历所有方法,根据你提到的这个列表找到最合适的?
  • 别管他说什么,这就是他引用的规范所说的 ;-)
【解决方案2】:

第二种方法获胜。根据Java Language Specification (pdf)

第一阶段(§15.12.2.2)在不允许的情况下执行重载解决方案 装箱或拆箱转换,或使用变量arity方法调用。

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

【讨论】:

  • 好的,非常感谢。我相信我理解这背后的基本思想,尽管我从未听说过装箱和重载解析
【解决方案3】:

我猜了一半,但就像 thinksteep 所说,Integer... 实际上被视为Integer 数组,这意味着您的方法调用必须执行两次强制才能使其匹配第一个方法(将@ 987654323@s 到 Integers,并将您的参数列表视为一个数组,而不仅仅是两个不同的参数)。然而,no 需要强制才能使其匹配第二种方法。

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

【讨论】:

  • 所以,减少“拳击”或(我如何理解)“转换”会更好。所以编译器以最少的工作完成了 :) 他很懒 :D
  • 或多或少,但引用 JLS 的其他答案更具体。它遵循一个定义的强制类型顺序。
【解决方案4】:

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

【讨论】:

  • 感谢您的努力。所以你把它编译成一个 .class 并打开它来查看字节码,你检查了这两种方法之间的区别,对吧?这是非常聪明的,也是找出背后发生了什么的好方法。但是字节码有点复杂不是吗?
  • @aeduG 是的,在逻辑思考和 JSL 视角之后,我只是使用 DJDecompiler 检查它的 .class,这样做总是对我有很大帮助
  • @aeduG 是的,它很复杂,但 DJDecompiler 提供了它的字节码形式以及编译器扩展代码,例如将 Integer... a 转换为 Integer[] aSystem.out.println(a+"hello"); 转换为 System.out.println((new StringBuilder()).append(a).append("hello").toString());
【解决方案5】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-20
    • 2014-10-19
    • 2010-10-11
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 1970-01-01
    • 2013-04-19
    相关资源
    最近更新 更多