【问题标题】:Why ambiguous error when using varargs overloading with primitive type and wrapper class? [duplicate]使用原始类型和包装类的可变参数重载时为什么会出现模棱两可的错误? [复制]
【发布时间】:2016-04-03 10:51:03
【问题描述】:

我不明白为什么在案例 1 中,它没有给出编译错误,相反,在案例 2 (varargs) 中,它给出了编译错误。谁能详细说明编译器在这两种情况下有何不同?我浏览了很多关于它的帖子,但还无法理解。

案例#1

public class Test {

    public void display(int a) {
        System.out.println("1");
    }

    public void display(Integer a) {
        System.out.println("2");
    }

    public static void main(String[] args) {
        new Test().display(0);
    }
}

输出为: 1

案例#2

public class Test {

    public void display(int... a) {
        System.out.println("1");
    }

    public void display(Integer... a) {
        System.out.println("2");
    }

    public static void main(String[] args) {
        new Test().display(0);
    }
}

编译错误

The method display(int[]) is ambiguous for the type Test

【问题讨论】:

  • 是的,您的问题伴随着 AutoBoxing,因为 Java 不确定是否将 '0' 解释为 int 或 Integer...
  • @JayC667,同样的问题不在案例1中吗?我的意思是为什么在情况 1 java 肯定会调用 public void display(int a)?如何?为什么只有在案例 2 中它不确定?
  • 我最好的猜测是,由于 Java 的 AutoBoxing 功能,编译器也必须考虑 varargs/paramarrays 来进行 AutoBoxing 转换。因此编译器将提供两种转换,将参数放入数组中:1) 直接传递原始类型,传递新 int[] {0},从而匹配 display(int... a),2) 通过 AutoBoxing 获取 vararg 参数,将你的 0到 new Integer[] {0},匹配您的 display(Integer... a) 方法。
  • 现在有了这两个提议,编译器找不到一个比另一个匹配更好的方法签名,因此将它的不确定性抛回给你,欢迎你添加一个虚拟参数或显式地将你的参数转换为数组
  • @AR.3,不要将其标记为重复。您指定的那个是不同的。在这里,我询问了两种情况之间的差异,这在您将其标记为重复的问题中没有。

标签: java overloading wrapper variadic-functions primitive


【解决方案1】:

在您的第一个示例中,display(int) 方法在严格调用上下文中调用,而 display(Integer) 在松散调用上下文中调用(因为需要自动装箱)。因此编译器根据 JLS 选择display(int) 方法。这里解释了调用上下文JLS 5.3. Invocation Contexts

在第二个示例中,两个方法都在松散的调用上下文中调用,因此编译器需要找到最具体的方法JLS 15.12.2.5 Choosing the Most Specific Method。由于 int 不是 Integer 的子类型,因此没有最具体的方法,编译器会抛出编译错误。

你可以在这里找到我对类似编译错误的解释Method overload ambiguity with Java 8 ternary conditional and unboxed primitives

适用于本案例的部分:

确定适用的方法分为三个阶段。

第一阶段(§15.12.2.2)执行重载决议 允许装箱或拆箱转换,或使用变量 arity 方法调用。如果过程中没有找到适用的方法 这个阶段然后处理继续到第二阶段。

第二阶段(§15.12.2.3)执行重载决议,同时 允许装箱和拆箱,但仍禁止使用 变量arity方法调用。如果没有找到适用的方法 在此阶段,处理将继续到第三阶段。

第三阶段(§15.12.2.4)允许将重载与 可变参数方法、装箱和拆箱。

对于第一个示例,只有display(int) 方法在第一阶段匹配,因此它被选中。对于第二个示例,两种方法都在第三阶段匹配,因此选择最具体的方法算法开始发挥作用JLS 15.12.2.5 Choosing the Most Specific Method

m2 不是通用的,并且 m1 和 m2 可以通过变量 arity 应用 调用,其中 m1 的前 k 个变量参数类型 是 S1, ..., Sk 和 m2 的前 k 个可变参数类型 是 T1, ..., Tk,对于参数 ei,类型 Si 比 Ti 更具体 对于所有 i (1 ≤ i ≤ k)。另外,如果 m2 有 k+1 个参数,那么 m1 的第 k+1 个变量参数类型是 m2的第k+1个变量arity参数类型。

如前所述,没有最具体的方法,因为 int <: integer>

【讨论】:

    【解决方案2】:

    在 java 版本 1.5 之后,引入了一个很酷的功能,名为 autoboxing,它使编译器能够将 原始类型 转换为 包装器类型。因此,在编译过程中,这两种方法都是一样的。

        public void display(int... a) {
            System.out.println("1");
        }
    
        public void display(Integer... a) {
            System.out.println("2");
        }
    

    这两个函数将被视为相同的方法,因为autoboxing编译 期间执行。所以在 Java 中重载方法时要小心 Autoboxing

    你可以在这里找到更多..

    Best Practices Of Method Overloading

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-26
      • 1970-01-01
      • 2010-09-30
      • 1970-01-01
      • 2019-08-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多