为什么autoboxing会在Java中使一些调用变得模棱两可?

Hos*_*Aly 34 java compiler-construction autoboxing overloading

我今天注意到自动装箱有时会导致方法重载分辨率的模糊.最简单的例子似乎是这样的:

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); }
}
Run Code Online (Sandbox Code Playgroud)

编译时,会导致以下错误:

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); }
                                  ^
Run Code Online (Sandbox Code Playgroud)

修复此错误很简单:只使用显式自动装箱:

static void m(int a, boolean b) { f((Object)a, b); }
Run Code Online (Sandbox Code Playgroud)

这正确地按预期正确调用第一个重载.

那么为什么重载决策失败了呢?为什么编译器没有自动包装第一个参数,并正常接受第二个参数?为什么我必须明确请求自动装箱?

elj*_*nso 32

当您自己将第一个参数转换为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
}
Run Code Online (Sandbox Code Playgroud)

请记住,选择匹配方法的算法(编译时步骤2)是固定的,并在JLS中进行了描述.一旦进入阶段2,就没有选择性自动装箱或拆箱.编译器将找到所有可访问的方法(在这些情况下都是这两种方法)和适用的(再次是两种方法),然后只选择最具体的方法而不查看装箱/拆箱,这在这里是不明确的.


Bil*_*ard 5

编译器自动装箱了第一个参数。完成后,第二个参数就变得不明确了,因为它可以被视为布尔值或对象。

本页解释了自动装箱和选择要调用的方法的规则。编译器首先尝试选择一个根本不使用任何自动装箱的方法,因为装箱和拆箱会带来性能损失。如果不借助装箱就无法选择任何方法(如本例所示),则对该方法的所有参数都可以进行装箱。