Chapter: Java The Complete Reference - The Java Language - A Closer Look at Methods and Classes

| Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail |

Varargs: Variable-Length Arguments

Beginning with JDK 5, Java has included a feature that simplifies the creation of methods that need to take a variable number of arguments.

Varargs: Variable-Length Arguments

 

Beginning with JDK 5, Java has included a feature that simplifies the creation of methods that need to take a variable number of arguments. This feature is called varargs and it is short for variable-length arguments. A method that takes a variable number of arguments is called a variable-arity method, or simply a varargs method.

Situations that require that a variable number of arguments be passed to a method are not unusual. For example, a method that opens an Internet connection might take a user name, password, filename, protocol, and so on, but supply defaults if some of this information is not provided. In this situation, it would be convenient to pass only the arguments to which the defaults did not apply. Another example is the printf( ) method that is part of Java’s I/O library. As you will see in Chapter 20, it takes a variable number of arguments, which it formats and then outputs.

 

Prior to JDK 5, variable-length arguments could be handled two ways, neither of which was particularly pleasing. First, if the maximum number of arguments was small and known, then you could create overloaded versions of the method, one for each way the method could be called. Although this works and is suitable for some cases, it applies to only a narrow class of situations.

 

In cases where the maximum number of potential arguments was larger, or unknowable, a second approach was used in which the arguments were put into an array, and then the array was passed to the method. This approach is illustrated by the following program:

 

     Use an array to pass a variable number of

 

     arguments to a method. This is the old-style

 

     approach to variable-length arguments. class PassArray {

 

static void vaTest(int v[]) { System.out.print("Number of args: " + v.length +

 

     Contents: ");

 

for(int x : v) System.out.print(x + " ");

System.out.println();

 

}

 

public static void main(String args[])

 

{

 

     Notice how an array must be created to

 

hold the arguments.

int n1[]  = {  10   };  

int  n2[] =    {    1, 2,     3 };

int  n3[] =    {    };       

vaTest(n1); // 1 arg 

vaTest(n2); // 3 args 

vaTest(n3); // no args

 

}

 

}

The output from the program is shown here:

 

Number of args: 1 Contents: 10

 

Number of args: 3 Contents: 1 2 3

 

Number of args: 0 Contents:

 

In the program, the method vaTest( ) is passed its arguments through the array v. This old-style approach to variable-length arguments does enable vaTest( ) to take an arbitrary number of arguments. However, it requires that these arguments be manually packaged into an array prior to calling vaTest( ). Not only is it tedious to construct an array each time vaTest( ) is called, it is potentially error-prone. The varargs feature offers a simpler, better option.

 

A variable-length argument is specified by three periods (). For example, here is how vaTest( ) is written using a vararg:

 

static void vaTest(int ... v) {

 

This syntax tells the compiler that vaTest( ) can be called with zero or more arguments. As a result, v is implicitly declared as an array of type int[ ]. Thus, inside vaTest( ), v is accessed using the normal array syntax. Here is the preceding program rewritten using a vararg:

 

// Demonstrate variable-length arguments. 

class VarArgs {

 

// vaTest() now uses a vararg. 

static void vaTest(int ... v) {

 

System.out.print("Number of args: " + v.length + " Contents: ");

 

for(int x : v) System.out.print(x + " ");

 

System.out.println();

 

}

 

public static void main(String args[])

 

{

 

     Notice how vaTest() can be called with a

 

variable number of arguments.

vaTest(10); // 1 arg

vaTest(1, 2, 3); // 3 args

vaTest(); // no args

}

 

}

 

The output from the program is the same as the original version.

 

There are two important things to notice about this program. First, as explained, inside vaTest( ), v is operated on as an array. This is because v is an array. The … syntax simply tells the compiler that a variable number of arguments will be used, and that these arguments will be stored in the array referred to by v. Second, in main( ), vaTest( ) is called with different numbers of arguments, including no arguments at all. The arguments are automatically put in an array and passed to v. In the case of no arguments, the length of the array is zero.

A method can have “normal” parameters along with a variable-length parameter. However, the variable-length parameter must be the last parameter declared by the method. For example, this method declaration is perfectly acceptable:

 

int doIt(int a, int b, double c, int ... vals) {

 

In this case, the first three arguments used in a call to doIt( ) are matched to the first three parameters. Then, any remaining arguments are assumed to belong to vals.

Remember, the varargs parameter must be last. For example, the following declaration is incorrect:

 

int doIt(int a, int b, double c, int ... vals, boolean stopFlag) { // Error!

 

Here, there is an attempt to declare a regular parameter after the varargs parameter, which is illegal.

 

There is one more restriction to be aware of: there must be only one varargs parameter. For example, this declaration is also invalid:

 

int doIt(int a, int b, double c, int ... vals, double ... morevals) { // Error!

 

The attempt to declare the second varargs parameter is illegal.

 

Here is a reworked version of the vaTest( ) method that takes a regular argument and a variable-length argument:

 

// Use varargs with standard arguments. 


class VarArgs2 {

 

     Here, msg is a normal parameter and v is a

 

     varargs parameter.

 

static void vaTest(String msg, int ... v) { 

System.out.print(msg + v.length + " Contents: ");

 

for(int x : v) System.out.print(x + " ");

 

System.out.println();

 

}

 

public static void main(String args[])

 

{

 

vaTest("One vararg: ", 10); 

vaTest("Three varargs: ", 1, 2, 3); 

vaTest("No varargs: ");

 

 

The output from this program is shown here:

One vararg: 1 Contents: 10

Three varargs: 3 Contents: 1 2 3 No varargs: 0 Contents:

 

 

Overloading Vararg Methods

You can overload a method that takes a variable-length argument. For example, the following program overloads vaTest( ) three times:

 

// Varargs and overloading. 


class VarArgs3 {

 

static void vaTest(int ... v) { 


System.out.print("vaTest(int ...): " +

 

"Number of args: " + v.length + " Contents: ");

 

for(int x : v) System.out.print(x + " ");

 

System.out.println();

 

}

 

static void vaTest(boolean ... v) { 


System.out.print("vaTest(boolean ...) " +

 

"Number of args: " + v.length + " Contents: ");

 

for(boolean x : v) System.out.print(x + " ");

 

System.out.println();

 

}

 

static void vaTest(String msg, int ... v) { 


System.out.print("vaTest(String, int ...): " +

 

msg + v.length + " Contents: ");

 

for(int x : v) 


System.out.print(x + " ");

 

System.out.println();

 

}

 

public static void main(String args[])

 

{

 

vaTest(1, 2, 3); 

vaTest("Testing: ", 10, 20); 

vaTest(true, false, false);

 

}

 

}

 

The output produced by this program is shown here:

vaTest(int ...): Number of args: 3 Contents: 1 2 3 

vaTest(String, int ...): Testing: 2 Contents: 10 20 

vaTest(boolean ...) Number of args: 3 Contents: true false false


This program illustrates both ways that a varargs method can be overloaded. First, the types of its vararg parameter can differ. This is the case for vaTest(int ...) and vaTest(boolean ...). Remember, the ... causes the parameter to be treated as an array of the specified type. Therefore, just as you can overload methods by using different types of array parameters, you can overload vararg methods by using different types of varargs. In this case, Java uses the type difference to determine which overloaded method to call.

The second way to overload a varargs method is to add one or more normal parameters. This is what was done with vaTest(String, int ...). In this case, Java uses both the number of arguments and the type of the arguments to determine which method to call.

Varargs and Ambiguity

 

Somewhat unexpected errors can result when overloading a method that takes a variable-length argument. These errors involve ambiguity because it is possible to create an ambiguous call to an overloaded varargs method. For example, consider the following program:

 

 

    Varargs, overloading, and ambiguity.

 

 

 

    This program contains an error and will

 

    not compile!

 

class VarArgs4 {

 

static void vaTest(int ... v) { 

System.out.print("vaTest(int ...): " + "Number of args: " + v.length + " Contents: ");

 

for(int x : v) System.out.print(x + " ");

 

System.out.println();

 

}

 

static void vaTest(boolean ... v) { 


System.out.print("vaTest(boolean ...) " +

 

"Number of args: " + v.length + " Contents: ");

 

for(boolean x : v) System.out.print(x + " ");

 

System.out.println();

 

}

 

public static void main(String args[])

 

{

vaTest(1, 2, 3); // OK 

vaTest(true, false, false); // OK

 

vaTest(); // Error: Ambiguous!

 

}

 

}

 

In this program, the overloading of vaTest( ) is perfectly correct. However, this program will not compile because of the following call:

 

vaTest(); // Error: Ambiguous!

 

Because the vararg parameter can be empty, this call could be translated into a call to vaTest(int …) or vaTest(boolean …). Both are equally valid. Thus, the call is inherently ambiguous.

 

Here is another example of ambiguity. The following overloaded versions of vaTest( ) are inherently ambiguous even though one takes a normal parameter:

 

static void vaTest(int ... v) { // ...

 

static void vaTest(int n, int ... v) { // ...

 

Although the parameter lists of vaTest( ) differ, there is no way for the compiler to resolve the following call:

 

vaTest(1)

 

Does this translate into a call to vaTest(int …), with one varargs argument, or into a call to vaTest(int, int …) with no varargs arguments? There is no way for the compiler to answer this question. Thus, the situation is ambiguous.

 

Because of ambiguity errors like those just shown, sometimes you will need to forego overloading and simply use two different method names. Also, in some cases, ambiguity errors expose a conceptual flaw in your code, which you can remedy by more carefully crafting a solution.


Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.