effective-java-3rd-chinese/docs/notes/53. 明智审慎地使用可变参数.md
mr-dragon 68acbcf2e1 在每篇文章后面加2个换行
在每篇文章后面加2个换行,方便使用cat命令合并成一个文件的时候不会影响目录的生成
2019-06-14 13:56:01 +08:00

69 lines
3.9 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 53. 明智审慎地使用可变参数
  可变参数方法正式名称称为可变的参数数量方法「variable arity methods」 [JLS, 8.4.1],接受零个或多个指定类型的参数。 可变参数机制首先创建一个数组,其大小是在调用位置传递的参数数量,然后将参数值放入数组中,最后将数组传递给方法。
  例如,这里有一个可变参数方法,它接受一系列 int 类型的参数并返回它们的总和。如你所料, `sum(1,2,3)` 的值为 6 `sum()` 的值为 0
```java
// Simple use of varargs
static int sum(int... args) {
int sum = 0;
for (int arg : args)
sum += arg;
return sum;
}
```
  有时,编写一个需要某种类型的一个或多个参数的方法是合适的,而不是零或更多。 例如,假设要编写一个计算其多个参数最小值的方法。 如果客户端不传递任何参数,则此方法定义不明确。 你可以在运行时检查数组长度:
```java
// The WRONG way to use varargs to pass one or more arguments!
static int min(int... args) {
if (args.length == 0)
throw new IllegalArgumentException("Too few arguments");
int min = args[0];
for (int i = 1; i < args.length; i++)
if (args[i] < min)
min = args[i];
return min;
}
```
  该解决方案存在几个问题。 最严重的是,如果客户端在没有参数的情况下调用此方法,则它在运行时而不是在编译时失败。 另一个问题是它很难看。 必须在 args 参数上包含显式有效性检查,除非将 `min` 初始化为 `Integer.MAX_VALUE`,否则不能使用 for-each 循环,这也很难看。
  幸运的是,有一种更好的方法可以达到预期的效果。 声明方法采用两个参数,一个指定类型的普通参数,另一个此类型的可变参数。 该解决方案纠正了前一个示例的所有缺陷:
```java
// The right way to use varargs to pass one or more arguments
static int min(int firstArg, int... remainingArgs) {
int min = firstArg;
for (int arg : remainingArgs)
if (arg < min)
min = arg;
return min;
}
```
  从这个例子中可以看出,在需要参数数量可变的方法时,可变参数是有效的。可变参数是为 `printf` 方法而设计的,该方法与可变参数同时添加到 Java 平台中,以及包括经过改造的核心反射机制。`printf` 和反射机制都从可变参数中受益匪浅。
  在性能关键的情况下使用可变参数时要小心。每次调用可变参数方法都会导致数组分配和初始化。如果你从经验上确定负担不起这个成本,但是还需要可变参数的灵活性,那么有一种模式可以让你鱼与熊掌兼得。假设你已确定 95 的调用是三个或更少的参数的方法,那么声明该方法的五个重载。每个重载方法包含 0 到 3 个普通参数,当参数数量超过 3 个时,使用一个可变参数方法:
```java
public void foo() { }
public void foo(int a1) { }
public void foo(int a1, int a2) { }
public void foo(int a1, int a2, int a3) { }
public void foo(int a1, int a2, int a3, int... rest) { }
```
  现在你知道,在所有参数数量超过 3 个的方法调用中,只有 5% 的调用需要支付创建数组的成本。与大多数性能优化一样,这种技术通常不太合适,但一旦真正需要的时候,它是一个救星。
  `EnumSet` 的静态工厂使用这种技术将创建枚举集合的成本降到最低。这是适当的因为枚举集合为比特属性提供具有性能竞争力的替换performance-competitive replacement for bit fields是至关重要的 (详见第 36 条)。
  总之,当需要使用可变数量的参数定义方法时,可变参数非常有用。 在使用可变参数前加上任何必需的参数,并注意使用可变参数的性能后果。