
The varargs feature was included in the JDK v1.5.0 and up. Its utility lies in allowing a method to accept a variable number of arguments.
According to [[Sun 06]] varargs documentation:
As an API designer, you should use them [varargs methods] sparingly, only when the benefit is truly compelling. Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called.
Retrofitting old methods containing final array parameters with varargs is not always a good idea. This is because if some method did not accept an argument of a particular type, it may be possible to override the compile-time checking so that with the use of generic varargs, it now compiles cleanly. [[Bloch 08]]
Lastly, varargs should not be treated like command line arguments where throwing an IllegalArgumentException
at runtime is a plausible escape route.
Noncompliant Code Example
Overloading varargs methods can create confusion as shown in this noncompliant example. The programmer's intent was to invoke the variable argument (varargs) doSomething
method, but instead its overloaded, more specific form took precedence.
class OverloadedVarargs { private static void doSomething(boolean... bool) { System.out.print("Number of arguments: " + bool.length + ", Contents: "); for (boolean b : bool) System.out.print("[" + b + "]"); } private static void doSomething(boolean bool1, boolean bool2) { System.out.println("Overloaded method invoked"); } public static void main(String[] args) { doSomething(true, false); } }
Compliant Solution
Avoid overloading varargs methods. Use distinct method names so that the intended method gets invoked as prescribed by this compliant solution.
class NotOverloadedVarargs { private static void doSomething1(boolean... bool) { System.out.print("Number of arguments: " + bool.length + ", Contents: "); for (boolean b : bool) System.out.print("[" + b + "]"); } private static void doSomething2(boolean bool1, boolean bool2) { System.out.println("Overloaded method invoked"); } public static void main(String[] args) { doSomething1(true, false); } }
Noncompliant Code Example
This noncompliant example aims to compute the average time given a constant speed and an arbitrary number of distance arguments. Varargs are used with speed
as the first argument, followed by the distances
. This code relies on runtime checking to ensure that the number of arguments passed is at least two and does not hesitate in throwing an IllegalArgumentException
, if it is not. Aesthetically speaking, the for
loop has little syntactic sugar to offer.
private int getTime(int... args) { if (args.length < 2) throw new IllegalArgumentException("Too few arguments. Usage <speed> <distance 1> ... <distance n>"); int speed = args[0]; // first arg is speed int distance = 0; for(int i=1;i < args.length;i++) // add all distances starting arg 2 distance += args[i]; int time = distance / speed; // calculate average time return time; }
Compliant Solution
This compliant solution fixes the two parameters (speed
and distance
) and proceeds to accept a variable number of subsequent distances using a varargs argument. The resulting code is also concise since a for-each
idiom can be used now.
private int getTime(int speed, int distance,int... remainingDistances) { for(int i : remainingDistances) // iterate trough varargs distance += i; int time = distance / speed; return time; }
Noncompliant Code Example
This code snippet shows autoboxing in action when the doSomething
method is called with the primitive integer 1
as a parameter (converted to Integer
type). This can very well be a programming misfortune.
doSomething(1) private void doSomething(Integer... i) { System.out.println("autoboxed"); }
The compiler type-checks the arguments to a varargs method to ensure that all arguments are of the same type or object reference. To our dismay, the compile-time checking is ineffectual when two method signatures are used in particular [[Bloch 08]]:
ReturnType1 suspect1(Object... args) { } <T> ReturnType2 suspect2(T... args) { }
Compliant Solution
Be as specific as possible while defining the type of a varargs method in order to enforce strong type checking at compile time.
doSomething(1) private void doSomething(int... i) { // use int instead of Integer here System.out.println("specific"); }
Likewise, do not use generic types like Object
in varargs.
Exceptions
Sometimes, it is desirable to violate the "do not overload varargs methods" advice due to performance benefits (avoiding the cost of creation of an array instance and its initialization on every invocation of a varargs method). [[Bloch 08]]
public void foo() { } public void foo(int a1) { } public void foo(int a1, int a2, int... rest) { }
The idiom shown above avoids the pitfalls of incorrect method selection by using non-ambiguous method signatures and can thus be discreetly used where required.
Risk Assessment
Unmindful use of the varargs feature may create ambiguity and diminish code readability.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
DCL02-J |
low |
unlikely |
low |
P3 |
L3 |
Automated Detection
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[Sun 06]] varargs
[[Bloch 08]] Item 42: "Use varargs judiciously"
[[Steinberg 05]] "Using the Varargs Language Feature"
DCL01-J. Use visually distinct identifiers 01. Declarations and Initialization (DCL) 02. Expressions (EXP)