Java 实参妙用
背景
可变参数(variable arity parameter,简称 vararg)方法是指形如 void doSomething(String... args)、可以接收数量不确定的同类型实参的方法。
需要明确的是:可变参数在编译后就是数组。也就是说,String... args 与 String[] args 的方法签名完全等价,调用方实际传入的必须是一个 String[]。正因为这个约束,List 不能作为实参直接传给可变参数方法,必须先转换为数组。
方案
将一个 List 传给可变参数方法,标准做法是:
- 通过
Collection.toArray(T[])把List转换为对应类型的数组; - 将该数组作为可变参数的实参传入。
示例
1 | public static void doSomething(String... args) { |
示例中 doSomething 声明了一个 String 类型的可变参数。我们先构造包含三个元素的列表,再调用 toArray(new String[0]) 得到 String[],最后将其作为实参传入,效果等价于:
1 | doSomething("arg1", "arg2", "arg3"); |
关键点解析
为什么要传入一个空数组
toArray(T[] a) 的契约是:
- 若传入数组的长度小于集合元素个数,则忽略该数组,新建一个同类型、长度恰好等于元素个数的数组返回;
- 若长度足够,则直接复用传入的数组(多余位置置为
null)。
由于列表长度在运行前通常未知,传入 new String[0] 可以让方法内部直接分配一个大小正确的新数组,既避免了容量不足,也不必额外计算 size()。
一个常见的替代写法是 myList.toArray(new String[myList.size()])。它在语义上同样正确,但存在两个小问题:一是需要先清零再填充,二是若 size() 与实际元素个数不一致(如并发修改)会产生尾部的 null 元素。因此,toArray(new String[0]) 是更推荐的写法;现代 HotSpot 虚拟机还会对该模式做专门优化,性能通常不逊于预分配大小的写法。
类型必须匹配
数组的元素类型必须与可变参数的组件类型一致,否则会在编译期报错:
1 | static void printInts(Integer... args) {} |
基本类型的可变参数(如 int...)需要的是 int[],Integer[] 不会自动拆箱为 int[]。
其他注意事项
- 空列表:
toArray(new String[0])会返回长度为 0 的数组,调用可变参数方法是安全的,方法内args.length == 0。 - 元素为
null:数组会原样保留null元素,方法内部若对元素做解引用需自行判空。 - 列表本身为
null:会抛出NullPointerException,建议在入口处校验。 - 重载歧义:若同时存在
doSomething()与doSomething(String...),无参调用会优先匹配前者;直接传null时需注意编译器可能匹配到数组版本。 - 无需担心副作用:
toArray返回的是新数组,被调用方对数组元素的修改不会影响原List。
更简洁的写法
如果只是为了构造一组参数,而不必先使用 List,可以直接用以下方式,省去转换步骤:
1 | doSomething("arg1", "arg2", "arg3"); // 直接列举 |
小结
- 可变参数的本质就是数组,因此接受
String...的方法实参必须是一个String[]; - 将
List传入可变参数方法的统一写法是list.toArray(new String[0]); - 注意组件类型匹配(尤其是基本类型)、
null处理以及重载带来的匹配歧义。