为什么自动装箱会使 Java 中的某些调用模棱两可?
我今天注意到自动装箱有时会导致方法重载解决方案的歧义.最简单的例子似乎是这样的:
I noticed today that auto-boxing can sometimes cause ambiguity in method overload resolution. The simplest example appears to be this:
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); }
^
这个错误的修复很简单:只需使用显式自动装箱:
The fix to this error is trivial: just use explicit auto-boxing:
static void m(int a, boolean b) { f((Object)a, b); }
按预期正确调用第一个重载.
Which correctly calls the first overload as expected.
那么为什么重载解析失败了?为什么编译器不自动装箱第一个参数,并正常接受第二个参数?为什么我必须明确请求自动装箱?
So why did the overload resolution fail? Why didn't the compiler auto-box the first argument, and accept the second argument normally? Why did I have to request auto-boxing explicitly?
推荐答案
当你自己将第一个参数强制转换为 Object 时,编译器将匹配方法而不使用自动装箱(JLS3 15.12.2):
When you cast the first argument to Object yourself, the compiler will match the method without using autoboxing (JLS3 15.12.2):
第一阶段(§15.12.2.2)执行未经允许的重载决议装箱或拆箱转换,或使用可变参数方法调用.如果没有适用的方法在这个阶段发现然后处理继续到第二个阶段.
The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.
如果你不显式地强制转换,它会进入第二阶段,试图找到匹配的方法,允许自动装箱,然后确实是模棱两可,因为你的第二个参数可以通过布尔值或对象匹配.
If you don't cast it explicitly, it will go to the second phase of trying to find a matching method, allowing autoboxing, and then it is indeed ambiguous, because your second argument can be matched by boolean or Object.
第二阶段(§15.12.2.3)执行重载决议,同时允许装箱和拆箱,但仍然排除使用可变参数方法调用.
The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation.
为什么在第二阶段,编译器不选择第二种方法,因为不需要对布尔参数进行自动装箱?因为在找到这两种匹配方法后,仅使用子类型转换来确定这两种方法中最具体的方法,而不管最初为匹配它们而发生的任何装箱或拆箱(第 15.12.2.5 节).
Why, in the second phase, doesn't the compiler choose the second method because no autoboxing of the boolean argument is necessary? Because after it has found the two matching methods, only subtype conversion is used to determine the most specific method of the two, regardless of any boxing or unboxing that took place to match them in the first place (§15.12.2.5).
另外:编译器不能总是根据需要的自动(取消)装箱次数选择最具体的方法.它仍然可能导致模棱两可的情况.例如,这仍然是模棱两可的:
Also: the compiler can't always choose the most specific method based on the number of auto(un)boxing needed. It can still result in ambiguous cases. For example, this is still ambiguous:
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 阶段,就没有选择性的自动装箱或拆箱.编译器将定位 所有 可访问的方法(这两种情况下的两种方法)和适用的方法(同样是两种方法),然后才选择最具体的方法而不查看装箱/拆箱,即这里有歧义.
Remember that the algorithm for choosing a matching method (compile-time step 2) is fixed and described in the JLS. Once in phase 2 there is no selective autoboxing or unboxing. The compiler will locate all the methods that are accessible (both methods in these cases) and applicable (again the two methods), and only then chooses the most specific one without looking at boxing/unboxing, which is ambiguous here.
相关文章