Var-Arg Methods(Variable number of argument methods)

Up until Java 1.4, we could not declare a method with a varying number of arguments. If there is a change in the number of arguments required, a new method should be used with the required number of arguments. This increases the amount of code and reduces readability.

To overcome this problem Var-Arg methods were introduced in Java 1.5. Var-Arg methods allow us to declare a method which can accept a varying number of arguments. A Var-Arg method is declared as follows: m1(int... x). This method can be called by passing any number of int values, also by passing no values.

When a Var-Arg argument is passed into a Var-Arg method, the argument is internally converted into a one dimensional array. Hence within the Var-Arg method, we can differentiate the values using array index.

import java.lang.*;
import java.util.*;

class Test {
    public static void main(String[] args) {
        sumOfIntegers(); // VALID: var-arg methods can be called with no values
        sumOfIntegers(10); // VALID: var-arg methods can be called with any varying number of integers
        sumOfIntegers(10,20); // VALID: var-arg methods can be called with any varying number of integers
        sumOfIntegers(10,20,30,40,50); // VALID: var-arg methods can be called with any varying number of integers
    }

    public static void sumOfIntegers(int... args) {
        int sumTotal = 0;
        for(int i : args) {
            sumTotal += args[i];
        }
        System.out.println("----------------------------------------------------------");
        System.out.println("The sumOfIntegers() method was called with " + args.length + " arguments.");
        System.out.println("The sum of " + Arrays.toString(args) + " is " sumTotal + ".");
        System.out.println("----------------------------------------------------------");
    }
}

When declaring a Var-Arg method, the syntax of the Var-Arg is important:

import java.lang.*;
import java.util.*;

class Test {
    public static void main(String[] args) {
        sumOfIntegers(); // VALID: var-arg methods can be called with no values
        sumOfIntegers(10); // VALID: var-arg methods can be called with any varying number of integers
        sumOfIntegers(10,20); // VALID: var-arg methods can be called with any varying number of integers
        sumOfIntegers(10,20,30,40,50); // VALID: var-arg methods can be called with any varying number of integers
    }

    public static void sumOfIntegers(int... args) {} // VALID: Popular way of declaring a Var-Arg method
    public static void sumOfIntegers(int ... args) {} // VALID: The spaces are ignored by the compiler
    public static void sumOfIntegers(int ...args) {} // VALID: The spaces are ignore by the compiler
    public static void sumOfIntegers(int...args) {} // VALID: Since space is ignored by compiler, it is not necessary to have it
    public static void sumOfIntegers(int args...) {} // INVALID: The dots must come before 'args'
    public static void sumOfIntegers(int. ..args) {} // INVALID: The dots must be next to each other
    public static void sumOfIntegers(int .args..) {} // INVALID: The dots must be next to each other
}

Var-Arg methods can take a mix of Var-Arg arguments and normal arguments. However, when mixing Var-Arg arguments and normal arguments, the Var-Arg parameter should always come last.

import java.lang.*;
import java.util.*;

class Test {
    public static void main(String[] args) {
        sum(OCT, 1, 2);
        sum(HEX, 1, 2);
        sum(DEC, 1, 2);
        sum2(1, 2, 3, DEC);
    }

    public static void sum(String base, int... args) { // VALID: can use both normal and Var-Arg arguments, as long as Var-Arg argument comes last
        switch (base) {
            case "OCT":
                sumOfOct(args);
                break
            case "DEC":
                sumOfDec(args);
                break
            default:
                unsupportedType(base);
                break;
        }
    }

    public static void sum2(int... args, String base) { // INVALID: Var-Arg argument should always come last
        switch (base) {
            case "OCT":
                sumOfOct(args);
                break
            case "DEC":
                sumOfDec(args);
                break
            default:
                unsupportedType(base);
                break;
        }
    }

    private void sumOfOct(int... args) {}

    private void sumOfDec(int... args) {}

    private void unsupportedType(String base) {}

}

When using a Var-Arg method, only one Var-Arg argument can be used as parameters, you cannot use multiple Var-Arg arguments in one method.

import java.lang.*;
import java.util.*;

class Test {
    public static void m1(int... x, double... d) {} // INVALID: cannot have more that one Var-Arg argument
}

Within a class, we cannot have more than one method with the same signature. Each method signature within a class must be unique. It’s important to note that the return type is not considered part of the method signature. So even if the methods have a different return type, they are still considered the same, if they have the same method signature in a class.

import java.lang.*;
import java.util.*;

class Test {
    public static void m1(int x) {} // INVALID: cannot have more that one method with the same signature: m1(int)
    public static int m1(int x) { return 0; } // INVALID: even though they have different return type, they still have the same signature: m1(int)
}

The same rule applies when using Var-Arg methods, since they are internally converted to arrays, you cannot have other methods with the same signature in the same class. If we did this, we would get a compile time error stating "cannot declare both m1(int[]) and m1(int...)"

import java.lang.*;
import java.util.*;

class Test {
    public static void m1(int... x) {} // INVALID: cannot have more that one method with the same signature: m1(int[])
    public static int m1(int... x) { return 0; } // INVALID: even though it has a different return type, it still have the same signature: m1(int[])
    public static void m1(int[] x) { return 0; } // INVALID: even though it is declared using array, it matches signature of Var-Arg methods: m1(int[])
}

There are cases where there may be some ambiguity, such as when methods have different signatures, but match in certain cases. Since Var-Arg method can take from 0 arguments to as many as the developer would require, if there is another method in the same class that is named the same as the Var-Arg method and takes the same data type, then you won’t get a compile time error, since their signatures are different. In this case when the methods are called, the Var-Arg method gets the least priority, it is only called if no other method matches the signature. It’s the same as the default case in a switch statement, where default is called if none of the case statements match the condition.

import java.lang.*;
import java.util.*;

class Test {
    public static void main(String[] args) {
        m1(); // Var-Arg method
        m1(1,2); // Var-Arg method
        m1(1); // General method
    }

    public static void m1(int... args) {
        System.out.println("Var-Arg method");
    }
    public static void m1(int arg) {
        System.out.println("General method");
    }
}

Equivalence between Var-Arg argument and one dimensional array

Where a one dimensional array is used as an argument, a Var-Arg argument can also be used in it’s place, because an array can be used as a Var-Arg parameter. An example of this is that you can replace main(String[] args), with main(String... args).

Table showing equivalence between Var-Arg argument and one dimensional array.
m1(int[] x)m1(int... x)
m1(new int[]{10})m1(new int[]{10})
m1(new int[]{10,20})m1(new int[]{10,20})
m1(new int[]{10,20,30})m1(new int[]{10,20,30})
m1(10)
m1(10,20)
m1(10,20,30)

While we can replace one dimensional array methods with Var-Arg methods, the opposite is not supported. This is because a one dimensional array method only accepts one dimensional arrays as arguments and will not accept any other values, whereas a Var-Arg methods accepts one dimensional arrays as well as 0 to many arguments. Therefore the one dimensional array method is not as flexible as the Var-Arg method.

Therefore m1(int[] x) can be replaced by m1(int... x), butm1(int... x) cannot be replaced by m1(int[] x)

In memory representation of Var-Arg arguments

Table showing in memory representation of Var-Arg arguments
Var-Arg argument In memory representation
Passing a group of int values: m1(int.. x) Represented in memory as a one dimensional array of int values int[] x
Passing a group of String values: m1(String... x) Represented in memory as a one dimensional array of String values String[] x
Passing a group of one dimensional arrays: m1(int[]... x) Represented in memory as a one dimensional array of arrays i.e. a 2D array int[][] x
Passing a group of two-dimensional arrays: m1(int[][]... x) Represented in memory as a one dimensional array of 2D arrays i.e. a 3D array int[][][] x
import java.lang.*;
import java.util.*;

class Test {
    public static void main(String[] args) {
        m1(new int[]{10,20,30},new int[]{40,50,60});
    }

    public static void m1(int[]... args) {
        for(int[] val : args) {
            System.out.println(val[0]); // prints 10, then 40
        }
    }
}