Tips
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,因此JDK 最好下载 JDK 9以上的版本。java
可变参数方法正式名称称为可变的参数数量方法『variable arity methods』 [JLS, 8.4.1],接受零个或多个指定类型的参数。 可变参数机制首先建立一个数组,其大小是在调用位置传递的参数数量,而后将参数值放入数组中,最后将数组传递给方法。git
例如,这里有一个可变参数方法,它接受一系列int类型的参数并返回它们的总和。如你所料,sum(1,2,3)
的值为6,sum()
的值为0:github
// Simple use of varargs static int sum(int... args) { int sum = 0; for (int arg : args) sum += arg; return sum; }
有时,编写一个须要某种类型的一个或多个参数的方法是合适的,而不是零或更多。 例如,假设要编写一个计算其多个参数最小值的方法。 若是客户端不传递任何参数,则此方法定义不明确。 你能够在运行时检查数组长度:数组
// 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循环,这也很难看。性能优化
幸运的是,有一种更好的方法能够达到预期的效果。 声明方法采用两个参数,一个指定类型的普通参数,另外一个此类型的可变参数。 该解决方案纠正了前一个示例的全部缺陷:性能
// 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个时,使用一个可变参数方法:设计
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%的调用须要支付建立数组的成本。与大多数性能优化同样,这种技术一般不太合适,但一旦真正须要的时候,它是一个救星。rest
EnumSet
的静态工厂使用这种技术将建立枚举集合的成本降到最低。这是适当的,由于枚举集合为比特属性提供具备性能竞争力的替换(performance-competitive replacement for bit fields)是相当重要的(条目 36)。code
总之,当须要使用可变数量的参数定义方法时,可变参数很是有用。 在使用可变参数前加上任何须需的参数,并注意使用可变参数的性能后果。