当前位置: 首页 > 面试题库 >

为什么自动装箱会使Java中的某些调用变得模棱两可?

蔚元明
2023-03-14
问题内容

我今天注意到,自动装箱有时会在方法重载解析中引起歧义。最简单的示例如下所示:

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方法调用。如果在此阶段未找到适用的方法,则处理将继续进行到第二阶段。

如果您没有明确地转换它,它将进入尝试查找匹配方法的第二阶段,允许自动装箱,然后它的确是模棱两可的,因为您的第二个参数可以通过布尔值或对象进行匹配。

第二阶段(第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阶段,就不会选择自动装箱或拆箱。编译器将找到 所有
可访问的方法(在这两种情况下均为这两种方法)和适用的方法(再次是这两种方法),然后才选择最具体的方法,而无需查看装箱/拆箱,此处不明确。



 类似资料:
  • 问题内容: 当我执行以下操作时, -包含一个元素,它是一个。 -不编译(错误:构造函数未定义) -包含7个元素,它们是对象 这是代码: 问题: 编译器为什么不自动将to 中的元素装箱并创建一个?这是什么原因呢?是我的愚蠢还是其他原因? 问题答案: 区别在于本身是,而是对对象的引用数组。 方法采用某种类型的变量参数,没有上限。该方法的擦除为。这意味着它将接受从扩展的任何类型的可变数量的参数。 由于不

  • 问题内容: 自动装箱是Java编译器在原始类型及其对应的对象包装器类之间进行的自动转换。例如,将int转换为Integer,将double转换为Double,依此类推。如果转换结果相反,则称为拆箱。 那么,为什么我们需要它?为什么要在Java中使用自动装箱和拆箱呢? 问题答案: 需要一些上下文来充分理解其背后的主要原因。 基元与类 Java中的原始变量包含值(整数,双精度浮点二进制数等)。由于这些

  • 问题内容: 我想将原语转换为字符串,然后尝试: 失败并显示以下错误: 现在,我知道原语不是引用类型(即不是对象),因此不能有方法。但是,Java 5引入了自动装箱和拆箱功能(一种C#…我在C#中从未喜欢过,但这不重要)。因此,通过自动装箱,我希望上面将myInt转换为Integer,然后在其上调用toString()。 此外,我相信C#允许这样的调用,除非我记错了。这仅仅是Java的自动装箱/拆箱

  • 问题内容: 为什么第二段代码更快? 问题答案: 自动装箱使用,内部将Integer对象缓存为小整数(默认情况下为-128至127,但是最大值可以使用“ java.lang.Integer.IntegerCache.high”属性进行配置-请参见Integer.valueOf的源代码) ,因此与直接调用不同。因为在调用之前可以快速检查整数值的大小,所以直接调用要快一些(尽管如果您有很多小整数,它会使

  • 问题内容: 据我了解,以下代码应该打印,但是当我运行它时,它可以打印。 根据JLS§5.1.7。装箱转换: 如果该值被装箱是,,一个,或一个在所述范围内对或或之间号和(含),然后让和是任何两个装箱转换的结果。总是这样的。 但是,在通过反射调用方法的情况下,始终通过创建框值。 请帮助我理解这一点。 问题答案: 调用将 始终 返回一个新的。所有返回的原语都装箱。 …如果[return]值具有原始类型,