Java方法重载和varargs

时间:2016-07-23 12:05:04

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

我试图理解方法重载,我有这些方法。

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 a   int ... a   int ... a   int ... a

据我所知,该程序不应该编译,因为模糊不清,但无论如何都是如此。编译器不应该抛出错误吗?

4 个答案:

答案 0 :(得分:4)

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

问题的其余部分是:为什么stack.method(null);甚至编译?

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

所以:

stack.method();

界面中method()签名的完全匹配。与method(int...)不一致,因为仅当其他人不匹配时才会考虑varargs。

stack.method(1);

匹配method(int)。由于与上述相同的原因,并不含糊。

stack.method(5,6);

匹配method(int...),因为没有一个非varargs匹配,但是varargs匹配了。

stack.method(null);

见前面的解释。

stack.method(a);

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

答案 1 :(得分:3)

方法重载分辨率有三个阶段。第一阶段和第二阶段不考虑使用varargs(也称为变量arity方法)作为候选者的方法,因此,只有在没有找到没有varargs的匹配方法的情况下,编译器才会将varargs视为候选方法。 / p>

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

  

15.12.2. Compile-Time Step 2: Determine Method Signature

     

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

     

可能有多种这样的方法,在这种情况下最多   选择特定的一个。描述符(签名加返回类型)   最具体的方法是在运行时使用的方法来执行   方法调度。

     

如果通过严格调用之一适用,则该方法适用   (§15.12.2.2),松散调用(§15.12.2.3),或变量arity   调用(§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)执行重载解析而不允许装箱或拆箱转换,或使用变量arity   方法调用。如果在此阶段没有找到适用的方法   然后处理继续到第二阶段。   这保证了在Java SE 5.0之前在Java编程语言中有效的任何调用都不会被认为是不明确的   作为引入变量arity方法的结果,隐含的   装箱和/或拆箱。但是,声明变量arity   方法(第8.4.1节)可以更改为给定方法方法选择的方法   调用表达式,因为变量arity方法被视为一个   固定arity方法在第一阶段。例如,声明   m(Object ...)在已经声明m(Object)的类中导致   不再为某些调用表达式选择m(Object)(例如   如m(null)),因为m(Object [])更具体。

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

  4.   
  5. 第三阶段(§15.12.2.4)允许重载与变量arity方法,装箱和拆箱相结合。

  6.   

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:1)

没有问题没有歧义:<!DOCTYPE html> <html> <head> <title>Contact Form | PHP, AJAX and MySQL</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" /> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> </head> <body> <br /><br /> <div class="container" style="width:500px;"> <form id="submit_form"> <label for="name">Name</label> <input type="text" name="name" id="name" class="form-control" /> <br /> <label for="message">Message</label> <textarea name="message" id="message" class="form-control"></textarea> <br /> <input type="submit" name="submit" id="submit" class="btn btn-info" value="Submit" /> <span id="error_message" class="text-danger"></span> <span id="success_message" class="text-success"></span> </form> </div> </body> </html> <script> jQuery(function($){ $('form#submit_form').submit(function(e){ e.preventDefault(); var name = $(this).find('#name').val(), message = $(this).find('#message').val(); if(name == '' || message == '') { $('#error_message').html("All Fields are required"); } else { $('#error_message').html(''); $.ajax({ url:"mailer.php", method:"POST", data:{ name: name, message: message }, success:function(data){ $("form").trigger("reset"); $('#success_message').fadeIn().html(data).fadeOut(3000); } }); } }); }); </script> 很好,因为该方法表示许多整数,传递passing "(5,6)"也很好,因为a是一个整数数组"(a)"也很好passing"(null)"可以转换为null之类的任何引用类型,因此可以在期望int []的地方使用;

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

integer []

前两个方法调用是自解释的,他们调用方法

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

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