我今天注意到自动装箱有时会导致方法重载分辨率的模糊.最简单的例子似乎是这样的:
public class Test { static void f(Object a, boolean b) {} static void f(Object a, Object b) {} static void m(int a, boolean b) { f(a,b); } }
编译时,会导致以下错误:
Test.java:5: reference to f is ambiguous, both method f(java.lang.Object,boolean) in Test and method f(java.lang.Object,java.lang.Object) in Test match static void m(int a, boolean b) { f(a, b); } ^
修复此错误很简单:只使用显式自动装箱:
static void m(int a, boolean b) { f((Object)a, b); }
这正确地按预期正确调用第一个重载.
那么为什么重载决策失败了呢?为什么编译器没有自动包装第一个参数,并正常接受第二个参数?为什么我必须明确请求自动装箱?
当您自己将第一个参数转换为Object时,编译器将匹配该方法而不使用自动装箱(JLS3 15.12.2):
第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换,或使用变量arity方法调用.如果在此阶段没有找到适用的方法,则处理继续到第二阶段.
如果你没有明确地转换它,它将进入第二阶段试图找到一个匹配的方法,允许自动装箱,然后它确实是模棱两可的,因为你的第二个参数可以匹配boolean或Object.
第二阶段(§15.12.2.3)执行重载解析,同时允许装箱和拆箱,但仍然排除使用变量arity方法调用.
为什么在第二阶段,编译器不会选择第二种方法,因为不需要自动装箱布尔参数?因为在找到两种匹配方法之后,只使用子类型转换来确定两者的最具体方法,无论是否首先匹配它们的装箱或拆箱(第15.12.2.5节).
另外:编译器不能总是根据所需的自动(非)拳击次数选择最具体的方法.它仍然可能导致模糊的情况.例如,这仍然是模棱两可的:
public class Test { static void f(Object a, boolean b) {} static void f(int a, Object b) {} static void m(int a, boolean b) { f(a, b); } // ambiguous }
请记住,选择匹配方法的算法(编译时步骤2)是固定的,并在JLS中进行了描述.一旦进入阶段2,就没有选择性自动装箱或拆箱.编译器将找到所有可访问的方法(在这些情况下都是这两种方法)和适用的(再次是两种方法),然后只选择最具体的方法而不查看装箱/拆箱,这在这里是不明确的.