J2SE 1.5提供了“Varargs”机制。借助这一机制,可以定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。本文介绍这一机制的使用方法,以及这一机制与数组、泛型、重载之间的相互作用时的若干问题。
到J2SE 1.4为止,一直无法在Java程序里定义实参个数可变的方法——因为Java要求实参(Arguments)和形参(Parameters)的数量和类 型都必须逐一匹配,而形参的数目是在定义方法时就已经固定下来了。尽管可以通过重载机制,为同一个方法提供带有不同数量的形参的版本,但是这仍然不能达到 让实参数量任意变化的目的。 然而,有些方法的语义要求它们必须能接受个数可变的实参——例如著名的main方法,就需要能接受所有的命令行参数为实参,而命令行参数的数目,事先根本无法确定下来。 对于这个问题,传统上一般是采用“利用一个数组来包裹要传递的实参”的做法来应付。 1. 用数组包裹实参 “用数组包裹实参”的做法可以分成三步:首先,为这个方法定义一个数组型的参数;然后在调用时,生成一个包含了所有要传递的实参的数组;最后,把这个数组作为一个实参传递过去。 这种做法可以有效的达到“让方法可以接受个数可变的参数”的目的,只是调用时的形式不够简单。 J2SE 1.5中提供了Varargs机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。 Varargs的含义 大体说来,“Varargs”是“variable number of arguments”的意思。有时候也被简单的称为“variable arguments”,不过因为这一种叫法没有说明是什么东西可变,所以意义稍微有点模糊。 2. 定义实参个数可变的方法 只要在一个形参的“类型”与“参数名”之间加上三个连续的“.”(即“...”,英文里的句中省略号),就可以让它和不确定个实参相匹配。而一个带有这样的形参的方法,就是一个实参个数可变的方法。 清单1:一个实参个数可变的方法- private static int sumUp(int... values) {
- }
- private static int sumUp(int... values) {
- }
注意,只有最后一个形参才能被定义成“能和不确定个实参相匹配”的。因此,一个方法里只能有一个这样的形参。另外,如果这个方法还有其它的形参,要把它们放到前面的位置上。
编译器会在背地里把这最后一个形参转化为一个数组形参,并在编译出的class文件里作上一个记号,表明这是个实参个数可变的方法。 清单2:实参个数可变的方法的秘密形态- private static int sumUp(int[] values) {
- }
- private static int sumUp(int[] values) {
- }
由于存在着这样的转化,所以不能再为这个类定义一个和转化后的方法签名一致的方法。
清单3:会导致编译错误的组合- private static int sumUp(int... values) {
- }
- private static int sumUp(int[] values) {
- }
- private static int sumUp(int... values) {
- }
- private static int sumUp(int[] 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 sumUp(int... values) {
- int sum = 0;
- for (int i = 0; i < values.length; i++) {
- sum += values[i];
- }
- return sum;
- }
- public class PrintfSample {
- public static void main(String[] args) {
- // 打印出“Pi:3.141593 E:2.718282”
- printOut("Pi:%f E:%f/n", Math.PI, Math.E);
- }
- private static void printOut(String format, Object... args) {
- // J2SE 1.5里PrintStream新增的printf(String format, Object... args)方法
- System.out.printf(format, args);
- }
- }
- public class PrintfSample {
- public static void main(String[] args) {
- // 打印出“Pi:3.141593 E:2.718282”
- printOut("Pi:%f E:%f/n", Math.PI, Math.E);
- }
- private static void printOut(String format, Object... args) {
- // J2SE 1.5里PrintStream新增的printf(String format, Object... args)方法
- System.out.printf(format, args);
- }
- }
- private static void testOverloading(int[] i) {
- System.out.println("A");
- }
- public static void main(String[] args) {
- testOverloading(1, 2, 3);// 编译出错
- }
- private static void testOverloading(int[] i) {
- System.out.println("A");
- }
- public static void main(String[] args) {
- testOverloading(1, 2, 3);// 编译出错
- }
- private static <T> void testVarargs(T... args) {
- // 编译出错
- }
- private static <T> void testVarargs(T... args) {
- // 编译出错
- }
造成这个现象的原因在于J2SE 1.5中的泛型机制的一个内在约束——不能拿用标识符来代表的类型来创建这一类型的实例。在出现支持没有了这个约束的Java版本之前,对于这个问题,基本没有太好的解决办法。
不过,传统的“用数组包裹”的做法,并不受这个约束的限制。 清单12:可以编译的变通做法- private static <T> void testVarargs(T[] args) {
- for (int i = 0; i < args.length; i++) {
- System.out.println(args[i]);
- }
- }
- private static <T> void testVarargs(T[] args) {
- for (int i = 0; i < args.length; i++) {
- System.out.println(args[i]);
- }
- }
- 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 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) {
- }
- }
- 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) {
- }
- }
- public class OverloadingSampleC {
- public static void main(String[] args) { /* 编译出错 */
- testOverloading(1, 2); /* 还是编译出错 */
- testOverloading(new Integer(1), new Integer(2));
- }
- private static void testOverloading(int... args) {
- }
- private static void testOverloading(Integer... args) {
- }
- }
- public class OverloadingSampleC {
- public static void main(String[] args) { /* 编译出错 */
- testOverloading(1, 2); /* 还是编译出错 */
- testOverloading(new Integer(1), new Integer(2));
- }
- private static void testOverloading(int... args) {
- }
- private static void testOverloading(Integer... args) {
- }
- }