Java Varargs 中的方法重载和歧义
先决条件——可变参数,方法重载
Varargs 中的方法重载
重载允许不同的方法具有相同的名称,但不同的签名,其中签名可能因输入参数的数量或输入参数的类型或两者而异。我们可以通过以下方式重载一个接受可变长度参数的方法:
- 案例 1 – 只有可变参数的方法:在这种情况下, Java使用类型差异来确定调用哪个重载方法。如果一个方法签名严格来说比另一个更具体,那么Java会选择它而不会出错。
//Java program to illustrate //method overloading in varargs public class varargsDemo { public static void main(String[] args) { fun(); } //varargs method with float datatype static void fun(float... x) { System.out.println("float varargs"); } //varargs method with int datatype static void fun(int... x) { System.out.println("int varargs"); } //varargs method with double datatype static void fun(double... x) { System.out.println("double varargs"); } }
输出:
int varargs
此输出是由于 int 比 double 更具体。如 JLS 第 15.12.2.5 节所述,如果多个成员方法既可访问又适用于方法调用,则有必要选择一个为运行时方法分派提供描述符。 Java编程语言使用根据类型提升选择最具体的方法的规则。在这种情况下,以下规则定义了原始类型之间的直接超类型关系:
- 双>浮动
- 浮动>长
- 长 > 整数
- 整数 > 字符
- 整数 > 短
- 短>字节
- 案例 2 – 带有可变参数和其他参数的方法在这种情况下, Java使用参数的数量和参数的类型来确定调用哪个方法。
下面是三次重载 fun() 方法的Java程序:
// Java program to demonstrate Varargs // and overloading. class Test { // A method that takes varargs(here integers). static void fun(int ... a) { System.out.print("fun(int ...): " + "Number of args: " + a.length + " Contents: "); // using for each loop to display contents of a for(int x : a) System.out.print(x + " "); System.out.println(); } // A method that takes varargs(here booleans). static void fun(boolean ... a) { System.out.print("fun(boolean ...) " + "Number of args: " + a.length + " Contents: "); // using for each loop to display contents of a for(boolean x : a) System.out.print(x + " "); System.out.println(); } // A method takes string as a argument followed by varargs(here integers). static void fun(String msg, int ... a) { System.out.print("fun(String, int ...): " + msg + a.length + " Contents: "); // using for each loop to display contents of a for(int x : a) System.out.print(x + " "); System.out.println(); } public static void main(String args[]) { // Calling overloaded fun() with different parameter fun(1, 2, 3); fun("Testing: ", 10, 20); fun(true, false, false); } }
输出:
fun(int ...): Number of args: 3 Contents: 1 2 3 fun(String, int ...): Testing: 2 Contents: 10 20 fun(boolean ...) Number of args: 3 Contents: true false false
可变参数和歧义
重载采用可变长度参数的方法时,有时会导致意外错误。这些错误包含歧义,因为这两种方法都是有效的调用候选者。编译器无法决定将方法调用绑定到哪个方法上。
// Java program to illustrate Varargs and ambiguity
class Test
{
// A method that takes varargs(here integers).
static void fun(int ... a)
{
System.out.print("fun(int ...): " +
"Number of args: " + a.length +
" Contents: ");
// using for each loop to display contents of a
for(int x : a)
System.out.print(x + " ");
System.out.println();
}
// A method that takes varargs(here booleans).
static void fun(boolean ... a)
{
System.out.print("fun(boolean ...) " +
"Number of args: " + a.length +
" Contents: ");
// using for each loop to display contents of a
for(boolean x : a)
System.out.print(x + " ");
System.out.println();
}
public static void main(String args[])
{
// Calling overloaded fun() with different parameter
fun(1, 2, 3); //OK
fun(true, false, false); //OK
fun(); // Error: Ambiguous!
}
}
在上面的程序中,fun() 的重载是完全正确的。但是,由于以下调用,该程序将无法编译:
fun(); // Error: Ambiguous!
根据(JLS 15.2.2),重载解析中使用了 3 个阶段:第一阶段执行重载解析,不允许装箱或拆箱转换,第二阶段执行重载解析,同时允许装箱和拆箱,第三阶段允许重载与变量组合arity 方法、装箱和拆箱。如果在这些阶段没有找到适用的方法,则会出现歧义。
上面的调用可以转换为对 fun(int ...) 或 fun(boolean ...) 的调用。两者同样有效,并且在重载解决的所有三个阶段之后都不会被解决,因为两种数据类型都不同。因此,调用本质上是模棱两可的。
另一个模棱两可的例子:
以下 fun( ) 的重载版本本质上是模棱两可的:
static void fun(int ... a) { // method body }
static void fun(int n, int ... a) { //method body }
在这里,虽然 fun() 的参数列表不同,但编译器无法解析以下调用:
fun(1)
此调用可能会解析为 fun(int ... a) 或 fun(int n, int ... a) 方法,从而产生歧义。为了解决像上面这样的歧义错误,我们需要放弃重载并简单地使用两个不同的方法名称。