关于可变参数
方法参数声明中的Type... 构造通常称为可变参数。在 JLS 中,它被称为 variable arity 参数。
列表中的最后一个形参是特殊的;它可能是一个可变参数,由类型后面的省略号表示。
如果最后一个形参是T类型的可变arity参数,则认为定义了T[]类型的形参。然后该方法是一个variable arity 方法。否则,它是一个 fixed arity 方法。变量 arity 方法的调用可能包含比形式参数更多的实际参数表达式。所有与变量 arity 参数之前的形式参数不对应的实际参数表达式都将被计算,并将结果存储到一个数组中,该数组将传递给方法调用。
为了在代码中进行说明,这是 varargs 允许您执行的操作:
static void f(int... nums) {
for (int num : nums) {
System.out.println(num);
}
}
//...
f(1,2,3); // prints "1", "2", "3"
相比之下,如果没有 varargs 构造,您必须这样做:
static void g(int[] nums) {
for (int num : nums) {
System.out.println(num);
}
}
//...
g(new int[] { 1, 2, 3 }); // prints "1", "2", "3"
可变参数是所谓的语法糖,它可以向您隐藏冗长。
所以回到你的问题,printMax(double... numbers) 和printmax(double numbers[]) 之间的区别在于第一个是 variable arity 方法,这意味着你可以给它可变数量的参数。后者是一个fixed arity方法,这意味着它将接受一个也是唯一的参数。
请注意上面关于T... 确实是T[] 的引用。也就是说,即使使用可变参数,您仍然可以执行以下操作:
f(new int[] { 1, 2, 3 }); // prints "1", "2", "3"
您在这里手动创建数组来保存可变参数。事实上,如果你对代码进行反编译,你会发现正如 JLS 所指定的那样,f 实际上采用了int[] 参数,而f(1, 2, 3) 实现为f(new int[] { 1, 2, 3 })。
另见
可变参数陷阱
如何解析可变参数是相当复杂的,有时它会做一些让你吃惊的事情。
考虑这个例子:
static void count(Object... objs) {
System.out.println(objs.length);
}
count(null, null, null); // prints "3"
count(null, null); // prints "2"
count(null); // throws java.lang.NullPointerException!!!
由于可变参数的解析方式,最后一条语句使用objs = null 调用,这当然会导致NullPointerException 使用objs.length。如果您想为可变参数提供一个 null 参数,您可以执行以下任一操作:
count(new Object[] { null }); // prints "1"
count((Object) null); // prints "1"
相关问题
以下是人们在处理可变参数时提出的一些问题示例:
关于何时使用可变参数
如上一节所示,可变参数可能很棘手。但是,在正确的情况下使用它们可以产生更简洁的代码。
这里引用Effective Java 2nd Edition,Item 42: Use varargs judiciously(作者强调):
教训很清楚。 不要对每个具有最终数组参数的方法进行改造; 仅在调用真正对可变长度的值序列进行操作时使用可变参数。
可变参数不仅会令人困惑,而且代价高昂。 Effective Java 2nd Edition 实际上建议为最常见的使用场景提供固定数量的重载。
假设您已确定 95% 的方法调用具有三个或更少的参数。然后声明该方法的五次重载,每个重载从零到三个普通参数,当参数数量超过三个时使用单个可变参数。
这本书的内容要深入得多,但基本上你应该只在真正有意义时才使用可变参数。即使在这些情况下,出于性能原因,您可能仍需要考虑提供固定数量的重载。
相关问题
API 链接
以下是一些使用可变参数的示例:
关于数组声明
请不要养成这样声明数组的习惯:
int x[];
你应该把括号放在type,而不是identifier:
int[] x;
请注意,这也是上述讨论中提到数组的方式,例如T[]int[]等
相关问题