我同意另一个答案的大部分内容,只有一部分尚未得到回答:为什么编译器有差异?
仔细一看,这不是 ecj 和 javac 之间的区别,而是不同版本的 JLS 之间的区别:
- 在合规性 1.7 或更低版本编译时,两个编译器都选择第一个(单参数)方法。
- 在合规性 1.8 下编译,两个编译器都选择第二种(可变参数)方法
让我们看一下生成的字节码:
7: invokestatic #8 // Method anyObject:()Ljava/lang/Object;
10: checkcast #9 // class "[Ljava/lang/Object;"
13: invokevirtual #10 // Method doThis:([Ljava/lang/Object;)V
(两个编译器也同意这些字节)
这就是说:Java 8 中的推理变得更加强大,现在能够推断出 to 的类型anyObject()
参数Object[]
。这可以从指令中看出,该checkcast
指令翻译回源代码如下:(Object[])anyObject()
. 这意味着也doThis(Object...)
可以在没有 varargs 魔法的情况下调用,但通过传递一个 type 的参数Object[]
。
现在这两种方法都适用于同一类别(“适用于固定数量调用”),并且在适用方法中搜索最具体的方法会选择第二种方法。
相比之下,Java 7 只允许将第二种方法作为可变参数调用来调用,但如果找到了固定参数匹配,则甚至不会尝试这样做。
以上还说明了如何使程序对 JLS 中的更改具有鲁棒性:
verify(mock, times(1)).doThis(Matchers.<Object>anyObject());
这将告诉所有版本的所有编译器选择第一个方法,因为现在它总是会看到doThis()
as的参数Object
——如果你真的无法避免这种不健康的重载,那就是:)