1

请你帮我理解为什么 testVargArgsAutoboxingPriority 的第一次调用的编译失败?

在第二次调用的情况下,编译器能够通过优先选择原始(第一个参数)而不是 Object 来选择正确的方法,但是在 varargs 参数添加之后编译器无法再进行选择。

失败消息是

\jdk1.6.0_45\bin\javac.exe ocjp6/AutoBoxingOldStyleVarargsPriority.java
ocjp6\AutoBoxingOldStyleVarargsPriority.java:7: reference to testVargArgsAutoboxingPriority is ambiguous, both method testVargArgsAutoboxing
Priority(java.lang.Integer,boolean...) in ocjp6.AutoBoxingOldStyleVarargsPriority and method testVargArgsAutoboxingPriority(int,boolean...)
in ocjp6.AutoBoxingOldStyleVarargsPriority match
      testVargArgsAutoboxingPriority( 5, true ); // the line compilation fails
      ^
1 error

完整的代码清单是

package ocjp6;

public class AutoBoxingOldStyleVarargsPriority
{
   public static void main( final String[] args )
   {
      testVargArgsAutoboxingPriority( 5, true ); // the line compilation fails
      testVargArgsAutoboxingPriority( 5 );
   }

   private static void testVargArgsAutoboxingPriority( Integer b, boolean... c )
   {}
   private static void testVargArgsAutoboxingPriority( int b, boolean... c )
   {}

   private static void testVargArgsAutoboxingPriority( Integer b )
   {}
   private static void testVargArgsAutoboxingPriority( int b )
   {}
}
4

2 回答 2

3

答案在于JLS - 15.12.2。编译时步骤 2:确定方法签名和上面的 @TheNewIdiot 答案。这里有一个更详细的解释:

对于方法:

private static void testVargArgsAutoboxingPriority( Integer b ) {}
private static void testVargArgsAutoboxingPriority( int b ) {}

编译器只需要第一阶段就可以知道要调用哪个方法。在第一阶段,它没有混合盒装和原始类型。

但是方法:

private static void testVargArgsAutoboxingPriority( Integer b, boolean... c ) {}
private static void testVargArgsAutoboxingPriority( int b, boolean... c ) {}

包含可变参数,编译器需要进入第三阶段尝试区分它们。但在第三阶段,不再能够区分装箱类型和相应的原始类型。

于 2013-07-13T18:29:46.653 回答
2

根据JLS 15.12.2 编译时步骤 2:确定方法签名

确定适用性的过程从确定可能适用的方法开始(§15.12.2.1)。

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

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

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

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

这确保了一个方法永远不会通过可变的方法调用来选择,如果它可以通过固定的方法调用来应用的话。

第三阶段(第 15.12.2.4 节)允许将重载与可变参数方法、装箱和拆箱相结合。

于 2013-07-13T18:11:59.357 回答