使用可变长度参数的 Java 重载

2022-09-02 21:57:14

为什么此代码中没有编译错误:

public class OverloadingVarArgs
{
    public void fun1(int... b)
    {
        System.out.println("int");
    }
    public void fun1(long... a)
    {
        System.out.println("long");
    }
    public static void main(String[] args)
    {
        OverloadingVarArgs obj = new OverloadingVarArgs();
        obj.fun1();
    }

}

但是这段代码给出了编译错误!

public class OverloadingVarArgs
{
    public void fun1(int... b)
    {
        System.out.println("int");
    }
    public void fun1(boolean... a)
    {
        System.out.println("boolean");
    }
    public static void main(String[] args)
    {
        OverloadingVarArgs obj = new OverloadingVarArgs();
        obj.fun1();
    }
}

我相信在这两种情况下都应该有编译错误,但事实并非如此。


答案 1

选择正确的重载方法的规则如下:

  • 基元加宽使用尽可能小的方法参数
  • 包装器类型无法加宽为其他包装类型
  • 您可以从 int 到整数,然后加宽到对象,但不能加宽到长整型
  • 加宽打败拳击,拳击打败Var-args。
  • 您可以装箱,然后加宽(整数可以通过整数成为对象)
  • 你不能加宽,然后盒子(一个int不能变成长)
  • 不能将 var-args 与加宽或装箱相结合

看看最后一条规则。不能将加宽或装箱与可变长度参数组合在一起。这意味着不能以任何方式操作类型,您必须按原样执行比较。 并且可以比较,没问题,编译器可以推断出这是两者中较小的一个。根据第一条规则,它将选择尽可能小的方法参数,因此它已经找到了通往方法的正确(也是唯一)路线。intlongint

但是,当您到达 和 时,由于 Java 的强类型化,两者之间不存在比较方法。由于不知道哪种类型最小,编译器完全不知道您指的是哪种方法。booleanint

更多可视化示例

让我们从编译器的角度一步一步地进行。首先,使用 和 。intlong

整型和长整型

步骤1 - 检查参数是否与任何参数匹配,如果是,则检查它完全匹配哪一个参数

好吧,意味着你可以传递到许多参数。在本例中,你已选择传递参数,因此你的调用同时匹配类型和类型。varargs00intlong

步骤2 - 尝试自动装箱或加宽。这应该有助于它找出要去哪一个

您正在使用 varargs,因此编译器知道它不能这样做,根据最终规则。

步骤 3 - 尝试确定哪种类型最小

编译器能够将类型与类型 进行比较。由此,可以看出是最小的类型。intlongint

步骤 4 - 拨打电话

有了最小类型的知识,它就将值传递给方法进行执行。int

好了,现在让我们对 和 做同样的事情。booleanint

布尔值和整型

步骤1 - 检查参数是否与任何参数匹配,如果是,则检查它完全匹配哪一个参数

同样的故事。你没有通过任何东西,所以匹配两个参数。

步骤2 - 尝试自动装箱或加宽。这应该有助于它找出要去哪一个

如上所述,您不能这样做,因为您使用了 varargs。

步骤 3 - 尝试确定哪种类型最小

这是关键的区别。在这里,类型具有可比性。这意味着编译器不知道要通过参数最小类型调用哪个方法。因此,它一直无法找出正确的路线。

步骤 4 - 拨打电话

如果不知道要调用哪个方法,则无法继续执行并引发相应的异常。


答案 2

在第二个示例中,编译器无法确定要调用的最具体的方法

语言规范中解释了血腥的细节,但本质上,如果比较两个变量 arity (var-arg) 方法,那么如果方法 A 可以接受传递给方法 B 的参数,但不能接受相反的方式,那么方法 B 是最具体的。

在第一个示例中,应用了原始子类型的规则,这些规则是:

双>1浮子

浮>1

长 >1 整型

整型 >1 个字符

整型 >1

短 >1 字节

(其中>1 表示“直接超类型”)

在这里,我们可以看到 an 比 a 更具体,因此选择了您的方法。intlongfun1(int... b)

在第二个示例中,编译器在 和 之间进行选择。这些原始类型之间没有子类型关系,因此没有最具体的方法,并且“方法调用不明确,并且发生编译时错误”。(15.12.2.5 中的最后一行)。intboolean