解析Java的可变长参数列表及其使用时的注意点
Java 可变参数列表
class A {}
由于所有的类都继承于Object,可以以Object数组为参数的方法:
public class parameter { static void printArray(Object[] args){ for(Object obj : args){ System.out.print(obj + " "); } System.out.println(); } public static void main(String[] args){ printArray(new Object[] { new Integer(47), new Float(3.14), new Double(11.11) }); printArray(new Object[]{"one", "two", "there"}); printArray(new Object[]{new A(), new A(), new A()}); } }
对于Java SE5之后版本添加了特性可以如此写:
public class parameter { public static void printArray(Object... args){ for(Object obj : args){ System.out.print(obj + " "); } System.out.println(); } public static void main(String[] args){ printArray(new Integer(47), new Float(3.14), new Double(11.11)); printArray(47, 3.14F, 11.11); printArray("one", "two", "three"); printArray(new A(), new A(), new A()); printArray((Object[]) new Integer[]{1, 2, 3, 4}); printArray(); } }
可以使用Object以为的参数列表:
public class VarargType{ static void f(Character... args){ System.out.print(args.getClass()); System.out.println(" length " + args.length); } static void g(int... args){ System.out.print(args.getClass()); System.out.println(" length " + args.length); } public static void main(String[] args){ f('a'); f(); g(1); g(); System.out.println(" int [] " + new int[0].getClass()); } }
这是 Java 5 引入的一个特性,如果一个方法要接收的参数数量是不确定的,那么这个特性就可以派上用场了。
比如,在涉及IO操作的地方,基本上至少需要关闭两个流:输入、输出,我喜欢把流关闭的操作封装成下面的方法,这样只需一次调用就可以关闭多个流。
public static void closeSilent(Closeable... closeables) { for (Closeable closeable : closeables) { if (closeable != null) { try { closeable.close(); } catch (IOException ignored) { } } } }
这是我觉得这个特性唯一适合使用的地方,具备下面的特点:
这些参数具有相同的类型;
参数数量不确定,每一个都是可选的;
这些参数的用途都是一样的,比如上面都是执行关闭。
Java 可变长参数列表只能放在方法参数列表的最后。
Java可变长参数列表的实现
Java可变长参数列表的实现是通过编译器把把这些参数封装成一个数组来传递的。
比如上面的方法的签名实际上是: closeSilent(Closeable[] closeables) void 。
踩坑
有个方法,被 A、B 两个地方 A、B 调用,9 月,在 A 这里需要增加一个参数,当时脑子一抽疯,决定用可变长参数列表,觉得 B 那里不用改简单点,坑就这样埋下了。
最近要求 B 这里要新增两个参数,那就在方法的参数列表里继续加参数,这些参数的类型是不同的,所以可变长参数列表声明成 Object 类型的。
第一个坑就是在这个方法内取可变长参数的元素时,没考虑到有的参数是没传的,直接爆数组越位的异常了。马上就觉得可变长参数列表的不好了,那就不用了呗,改为常规的固定形式的参数传递。
改完之后,在测试环境测了都没问题。把生产环境的几个类替换之后,结果却报错了,方法找不到,一看方法签名,还是数组的,没有替换到。从源码看,那个调用的地方不需要更改,所以没想到要替换;由于测试环境是全量打包,所以不会出现问题。
方法的签名是在编译的时候就确定了的,源码层面看起来不需要改的不表示编译后的类也不需要替换了。
其实以前也听到过,在这种发包不规范的情况下,把源码里的一个常量值改了之后,只替换了这个定义常量的类文件,没有把所有引用这个常量的类文件重新编译替换,导致出现莫名其妙的问题。跟方法签名的情况本质上是一样的问题。