【问题标题】:Java method overloading and varargsJava 方法重载和可变参数
【发布时间】:2016-07-23 12:05:04
【问题描述】:

我正在尝试理解方法重载,并且我有这些方法。

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

//implementing interface method
@Override
public void method() {
    System.out.println("interface");
}

//varargs
public void method(int ... a){
    System.out.println("int ... a");
}

用这些参数调用它们后,

int[] a = new int[5];
stack.method();
stack.method(1);
stack.method(5,6);
stack.method(null);
stack.method(a);

我有这些结果:

界面
诠释一个
int ... 一个
int ... 一个
诠释...一个

据我所知,由于模棱两可,该程序不应该编译,但无论如何它都会编译。编译器不应该报错吗?

【问题讨论】:

  • int 是基本类型,null 是对象。如果您未指定任何基本类型,则每个基本类型都有一个默认值 (int = 0)。由于数组本身就是一个对象,因此 null 被解释为 vararg 参数作为基类型而不是引用,因此不能为 null。就这么简单

标签: java compiler-errors overloading variadic-functions ambiguous


【解决方案1】:

Eran 和 Bathsheba 已经说过为什么选择了各种不使用null 的人。

剩下的问题是:为什么stack.method(null); 甚至可以编译?

答案是它匹配可变参数签名,因为可变参数method(int...) 从编译器的角度来看实际上与method(int[]) 相同。由于数组是通过引用来引用的,所以null 可以用在需要int[] 的地方。

所以:

stack.method();

完全匹配界面中的method() 签名。与 method(int...) 没有歧义,因为仅当其他参数不匹配时才考虑可变参数。

stack.method(1);

匹配 method(int)。出于与上述相同的原因,并非模棱两可。

stack.method(5,6);

匹配 method(int...),因为没有非可变参数匹配,但可变参数匹配。

stack.method(null);

参见前面的解释。

stack.method(a);

match(int...) 匹配的原因与method(null0 匹配的原因相同:因为match(int...) 对于编译器而言实际上与match(int[]) 相同。

【讨论】:

    【解决方案2】:

    方法重载解析分为三个阶段。第一阶段和第二阶段不考虑使用可变参数的方法(也称为可变参数方法)作为候选方法,因此只有在没有找到匹配的方法没有可变参数时,编译器才会考虑使用可变参数的方法作为候选方法。

    因此,在第一个和第二个方法调用中,你的void method(int ... a)被忽略了,没有歧义。

    15.12.2. Compile-Time Step 2: Determine Method Signature

    第二步搜索上一步确定的类型 成员方法。此步骤使用方法的名称和参数 用于定位既可访问又适用的方法的表达式, 也就是说,可以在给定的情况下正确调用的声明 论据。

    这样的方法可能不止一种,在这种情况下,最 选择了特定的一个。的描述符(签名加返回类型) 最具体的方法是在运行时用于执行 方法分派。

    一个方法是适用的,如果它通过严格调用之一适用 (§15.12.2.2)、松散调用(§15.12.2.3)或可变数量 调用(§15.12.2.4)。

    某些包含隐式类型 lambda 的参数表达式 表达式(§15.27.1)或不精确的方法引用(§15.13.1)是 被适用性测试忽略,因为它们的含义不能被 在选择目标类型之前确定。

    虽然方法调用可能是一个poly表达式,但只有它的 参数表达式 - 不是调用的目标类型 - 影响 适用方法的选择。

    确定适用性的过程始于确定 可能适用的方法(§15.12.2.1)。

    流程的其余部分分为三个阶段,以确保 与之前的 Java 编程语言版本的兼容性 Java SE 5.0。阶段是:

    1. 第一阶段(§15.12.2.2)执行重载解决不允许装箱或拆箱转换,或使用可变参数 方法调用。如果在此阶段没有找到适用的方法 然后处理继续到第二阶段。 这保证了在 Java SE 5.0 之前的 Java 编程语言中有效的任何调用都不会被认为是模棱两可的 由于引入了可变数量方法,隐式 装箱和/或拆箱。但是,变量arity的声明 方法(第 8.4.1 节)可以更改为给定方法方法选择的方法 调用表达式,因为变量 arity 方法被视为 第一阶段的固定数量方法。例如,声明 m(Object...) 在已经声明 m(Object) 的类中导致 不再为某些调用表达式选择 m(Object)(例如 作为 m(null)),因为 m(Object[]) 更具体。

    2. 第二阶段(第 15.12.2.3 节)执行重载解决方案,同时允许装箱和拆箱,但仍排除使用变量 arity 方法调用。如果在此期间没有找到适用的方法 阶段然后处理继续到第三阶段。 这确保了一个方法永远不会通过可变 arity 方法调用来选择,如果它适用于通过固定 arity 方法 调用。

    3. 第三阶段(第 15.12.2.4 节)允许将重载与可变参数方法、装箱和拆箱结合使用。

    【讨论】:

      【解决方案3】:

      只有在所有其他可能性都用尽后,编译器才会考虑具有可变参数列表的方法。

      这些“其他可能性”以正常的方式考虑。

      因此在您的情况下没有歧义,因此编译器不会发出错误。

      【讨论】:

        【解决方案4】:

        不,这很好,没有歧义:passing "(5,6)" 很好,因为该方法需要许多整数,传递 "(a)" 也很好,因为 a 是一个整数数组 passing"(null)" 也很好,因为可以强制转换 null到任何引用类型,例如integer [],因此可以在您期望的地方使用 int [];

        所以所有这些调用都会调用第三种方法

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

        前两个方法调用是不言自明的,它们调用方法

        public void method(){
            System.out.println("interface");
        }
        

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

        分别

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-09
          • 1970-01-01
          • 2023-02-15
          • 1970-01-01
          相关资源
          最近更新 更多