代码之家  ›  专栏  ›  技术社区  ›  DanielBK

Java:允许使用一个varargs参数的函数和一个名称相同、类型相同的参数?[副本]

  •  2
  • DanielBK  · 技术社区  · 8 年前

    在准备Java认证考试时,我非常惊讶地看到Java允许这样做:

    public class Consumer {
    
        public void buy(Object o) {
            System.out.println("Buying one object");
        }
    
        public void buy(Object... o) {
            System.out.println("Buying multiple objects");
        }
    
        public static void main(String[] args) {
            Consumer consumer = new Consumer();
            consumer.buy(new Object());
            consumer.buy("a String");
        }
    
    }
    

    此类编译和运行良好。它会打印两次“购买一件物品”。实际上,我想看到一个编译器错误,因为这两个函数都可以使用。编译器如何在此处选择最佳匹配函数?当我只传递一个参数时,它是否总是选择非varargs函数?

    2 回复  |  直到 8 年前
        1
  •  9
  •   Community Mohan Dere    5 年前

    方法重载解析分为3个阶段。只有在第三个也是最后一个阶段,它才考虑使用varags的方法(例如 public void buy(Object... o) ),因此,如果在前两个阶段中的一个阶段中找到匹配方法,则会忽略varargs方法,并选择非varag匹配方法。

    因此,两个调用都会导致 public void buy(Object o) 被选中。

    当我只传递一个参数时,它是否总是选择非varargs函数?

    当您只传递一个参数时,它将始终选择非varargs方法,除非该参数的编译时类型是数组:

    Object[] arr = new Object[]{"a string"};
    consumer.buy(arr);
    

    经过 null 还会导致编译器选择varargs方法:

    consumer.buy(null);
    

    这是相关的 JLS 15.12.2. Compile-Time Step 2: Determine Method Signature 报价:

    确定适用性的过程从确定潜在适用的方法开始(§15.12.2.1)。然后,为了确保与Java SE 5.0之前的Java编程语言兼容,该过程将分三个阶段继续:

    1. 第一阶段执行过载解决 未经允许 装箱或取消装箱转换,或 变量arity方法调用的使用 . 如果在此阶段未找到适用的方法,则处理将继续到第二阶段。

      这保证了在Java SE 5.0之前的Java编程语言中有效的任何调用都不会因为引入变量arity方法、隐式装箱和/或取消装箱而被认为是不明确的。然而,变量arity方法的声明(§8.4.1)可能会更改为给定方法方法调用表达式选择的方法,因为变量arity方法在第一阶段被视为固定arity方法。例如,声明m(对象…)在已经声明了m(Object)的类中,由于m(Object[])更具体,因此不再为某些调用表达式(例如m(null))选择m(Object)。

    2. 第二阶段执行重载解析,同时允许装箱和拆箱,但仍然禁止使用变量arity方法调用。如果在此阶段未找到适用的方法,则处理将继续到第三阶段。

      这确保了如果方法通过固定arity方法调用适用,则永远不会通过变量arity方法调用来选择方法。

    3. 第三阶段允许重载与变量arity方法相结合 、装箱和拆箱。

        2
  •  1
  •   Marteng    8 年前

    在你的特殊情况下 编译器将仅选择 buy(Object... o) 如果传递给此函数的参数是数组(其中还包括表示数组的逗号分隔语法)。例如:

    Object o1 = new Object();
    Object o2 = new Object();
    Object[] oArray = new Object[]{o1, o2};
    
    buy((Object[]) null);  // will call the varargs function
    buy(new Object[]{o1}); // will call the varargs function
    buy(oArray);           // will call the varargs function
    buy(o1, o2);           // will call the varargs function
    
    buy((Object) null);    // will call the non-varargs function
    buy(o1);               // will call the non-varargs function