引言
因为Java要求实参(Arguments)和形参(Parameters)的个数和类型都必须逐一匹配,而形参在定义方法时就已经固定下来了,那么应该如何在Java程序里定义实参个数可变的方法呢?目前有以下三种解决方式:
- 方法重载;
- 数组参数Object[] args;
- 动态参数/可变参数Object… args;
说明:以上方法实现了实参个数可变,Object实现了参数类型可变。
一、方法重载
简介:重载允许在同一个类拥有许多只有形参列表不同的方法。然后,由编译器根据调用时的实参来选择到底要执行哪一个方法。
缺点:尽管可以通过重载机制,为同一个方法提供带有不同数量的形参的版本,但这仍不能达到让实参数量任意变化的目的。
举例:可以通过重载testOverloading方法,实现传入多种参数的可能。
public class OverloadingSampleA {
public static void main(String[] args) {
testOverloading(1);// 打印出A
testOverloading(1, 2);// 打印出B
}
private static void testOverloading(int i) {
System.out.println("A");
}
private static void testOverloading(int i, int j) {
System.out.println("B");
}
}
二、Object[] args
缺点:虽然只需一个方法,但在调用方法时,需要构造数组参数(先声明一个数组,再将参数一个个加到数组中),用起来太麻烦。
举例:最常见的是main方法,利用一个数组来包裹要传递的实参。
public static void main(String[] args) {
}
三、Object… args
J2SE 1.5中提供了Varargs机制(variable number of arguments),允许直接定义能和多个实参相匹配的形参。
3.1 定义
定义:类型… 参数名。
原理:编译器会把这个形参转化为一个数组形参,并在编译出的class文件里作上一个记号,表明这是个实参个数可变的方法。
注意:
- 由于存在这样的转化,所以不能再为这个类定义一个和转化后的方法签名一致的方法。
- 可变长参数必须是最后一个参数。
- 可变参数同时可以跟固定的参数混合使用,但是一个方法的参数中不能同时拥有2种类型的可变参数。
举例:以下两个方法“等效”,且不能在一个类下同时定义。
private static int sumUp(int... values) {
}
private static int sumUp(int[] values) {
}
3.2 调用
注意:“可变个数”也包括0个。
举例:以下两组调用等效,注意传0个参数时,等效传递一个空数组,而不是null。
sumUp(1, 3, 5, 7);
sumUp(new int[]{1, 2, 3, 4});
sumUp();
sumUp(new int[]{});
3.3 处理
说明:处理个数可变的实参的办法,和处理数组实参的办法基本相同。
举例:直接把int… values当成int[] values处理。
private static int sumUp(int... values) {
int sum = 0;
for (int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
3.4 传参
说明:可变参数和数组参数可以相互传参。
举例:sumUp定义int… values时可传入int[] values,sumUp定义int[] values时,也可传入int… values。
// 方式一
private static int getSumUp(int[] values) {
return sumUp(values);
}
private static int sumUp(int... values) {
int sum = 0;
for (int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
// 方式二
private static int getSumUp(int... values) {
return sumUp(values);
}
private static int sumUp(int[] values) {
int sum = 0;
for (int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
注意:不能在调用只支持用数组包裹实参的方法的时候,直接采用这种简明的调用方式,见下面例子。
private static void testOverloading(int[] i) {
System.out.println("A");
}
public static void main(String[] args) {
testOverloading(1, 2, 3);// 编译出错
}
3.5 泛型
简介:泛型可以在一定条件下把一个类型参数化。例如,可以在编写一个类的时候,把一个方法的形参的类型用一个标识符(如T)来代表, 至于这个标识符到底表示什么类型,则在生成这个类的实例的时候再行指定。
说明:泛型不能和可变形参配合使用,但是数组形参不受这个约束的限制。
举例:T… args报错,T[] args成功。
// 编译出错
private static <T> void testVarargs(T... args) {
}
// 编译通过
private static <T> void testVarargs(T[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
3.6 重载
说明:传统上的选择,基本是依照“特殊者优先”的原则来进行。 但是Varargs机制引入之后,完全可以出现两个版本都能匹配,在其它方面也别无二致,只是一个实参个数固定,而一 个实参个数可变的情况。此时,满足“实参个数固定的版本优先于实参个数可变的版本”。
举例:优先打印A和B,再打印C。
public class OverloadingSampleA {
public static void main(String[] args) {
testOverloading(1);// 打印出A
testOverloading(1, 2);// 打印出B
testOverloading(1, 2, 3);// 打印出C
}
private static void testOverloading(int i) {
System.out.println("A");
}
private static void testOverloading(int i, int j) {
System.out.println("B");
}
private static void testOverloading(int i, int... more) {
System.out.println("C");
}
}
注意:可能会有两个版本都能匹配,在其它方面也如出一辙,而且都是实参个数可变的冲突发生。
public class OverloadingSampleB {
public static void main(String[] args) {
testOverloading(1, 2, 3);// 编译出错
}
private static void testOverloading(Object... args) {
}
private static void testOverloading(Object o, Object... args) {
}
}
参考
java学习之九:方法中Object… args参数的含义
java中 Object… args 的理解