Easy Tutorial
❮ Mysql Different Nnodb Myisam 9Patch 9 Remove Black Line ❯

Java Varargs

Category Programming Technology

Java 5 introduced varargs, allowing methods to accept an indefinite number of arguments. Varargs is syntactic sugar in Java, essentially implemented based on arrays:

void foo(String... args);
void foo(String[] args);
// Method signature
([Ljava/lang/String;)V // public void foo(String[] args)

Defining Methods

When defining a method, append three dots after the last formal parameter to indicate it can accept multiple arguments, which are treated as an array. Several points need attention:

-

Varargs can only be the last parameter in a method, but it can be preceded by other parameters or none at all.

-

Since varargs must be the last parameter, a method can have at most one vararg.

-

Java's varargs are compiled into an array.

-

After compilation to bytecode, varargs appear as arrays in the method signature. These two method signatures are identical and cannot be overloaded. If both are present, they cannot be compiled. Varargs are compatible with arrays, but not vice versa.

public void foo(String... varargs){}

foo("arg1", "arg2", "arg3");

// The above is equivalent to the following call
foo(new String[]{"arg1", "arg2", "arg3"});

-

J2SE 1.5 introduced "Generics", which allows type parameterization under certain conditions. For example, a method's parameter type can be represented by a placeholder (e.g., T), and the actual type is specified when an instance of the class is created. This mechanism enhances code reuse and compile-time type checking. However, generics cannot be used with varargs. If the type of a vararg is represented by a placeholder, the compiler will throw a "generic array creation" error.

public class Varargs {

    public static void test(String... args) {
        for(String arg : args) {// Treated as an array with foreach loop
            System.out.println(arg);
        }
    }
    // Compile error
    // The variable argument type Object of the method must be the last parameter
    // public void error1(String... args, Object o) {}
    // public void error2(String... args, Integer... i) {}

    // Compile error
    // Duplicate method test(String...) in type Varargs
    // public void test(String[] args){}
}

Calling Varargs Methods

Calling a varargs method can have zero to any number of arguments; the compiler converts varargs into an array. You can also directly pass an array, as shown below:

public class Varargs {

    public static void test(String... args) {
        for(String arg : args) {
            System.out.println(arg);
        }
    }

    public static void main(String[] args) {
        test();// 0 arguments
        test("a");// 1 argument
        test("a","b");// Multiple arguments
        test(new String[] {"a", "b", "c"});// Directly passing an array
    }
}

Method Overloading

Prefer Fixed Parameters

When calling an overloaded method, if the call matches both a fixed parameter method and a varargs method, the fixed parameter method is chosen:

public class Varargs {

    public static void test(String... args) {
        System.out.println("version 1");
    }

    public static void test(String arg1, String arg2) {
        System.out.println("version 2");
    }
    public static void main(String[] args) {
        test("a","b");// version 2, prefers fixed parameter method
        test();// version 1
    }
}

Ambiguity with Multiple Varargs

When calling an overloaded method, if the call matches two varargs methods, a compile error occurs:

public class Varargs {

    public static void test(String... args) {
        System.out.println("version 1");
    }

    public static void test(String arg1, String... arg2) {
        System.out.println("version 2");
    }
    public static void main(String[] args) {
        test("a","b");// Compile error
    }
}

Method Overriding

Avoid Overloading with Varargs

Even though the compiler can prioritize fixed parameter methods, reading such code can still be misleading. Be cautious with overloading methods that include varargs.

Guard Against Null and Empty Values in Varargs

public class Client {  
     public void methodA(String str,Integer... is){       
     }  

     public void methodA(String str,String... strs){          
     }  

     public static void main(String[] args) {  
           Client client = new Client();  
           client.methodA("China", 0);  
           client.methodA("China", "People");  
           client.methodA("China");  //compile error
           client.methodA("China",null);  //compile error
     }  
}

Modify as follows:

public static void main(String[] args) {  
     Client client = new Client();  
     String[] strs = null;  
     client.methodA("China",strs);  
}

Ensuring the compiler recognizes the null value as String type allows successful compilation and reduces errors.

Follow Rules When Overriding Varargs Methods

package com;
public class VarArgsTest2 {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // Upcasting
        Base base = new Sub();
        base.print("hello");
        // No upcasting
        Sub sub = new Sub();
        sub.print("hello");//compile error
    }
}
// Base class
class Base {
    void print(String... args) {
        System.out.println("Base......test");
    }
}
// Subclass, overriding the parent class method
class Sub extends Base {
    @Override
    void print(String[] args) {
        System.out.println("Sub......test");
    }
}

The first example compiles because the base object upcasts the subclass object sub, and the parameter list is determined by the parent class. Direct calls from the subclass, however, cause a compile error because the compiler sees the subclass overrides the parent's print method, and it does not look further for matching signatures in the parent class once an override is found.

This is an exception where the overridden method's parameter list can differ from the parent's, violating the definition of overriding and leading to confusing errors.

Here are the conditions for overriding:

-

The overriding method must not narrow the access scope.

-

The parameter list must be identical to the overridden method (including explicit form).

-

The return type must be the same as, or a subtype of, the overridden method's return type.

-

The overriding method must not throw new checked exceptions or exceptions beyond those thrown by the overridden method, but it can throw fewer, more limited exceptions, or no exceptions.


Potential Issues

Using Object… as varargs:

public void foo(Object... args) {
    System.out.println(args.length);
}

foo(new String[]{"arg1", "arg2", "arg3"}); //3
foo(100, new String[]{"arg1", "arg1"}); //2

foo(new Integer[]{1, 2, 3}); //3
foo(100, new Integer[]{1, 2, 3}); //2
foo(1, 2, 3); //3
foo(new int[]{1, 2, 3}); //1

int[] cannot be cast to Object[] and is treated as a single array object; Integer[] can be cast to Object[] and is treated as an object array.


Considerations for Reflective Method Calls

public class Test {
    public static void foo(String... varargs){
        System.out.println(args.length);
    }

    public static void main(String[] args){
        String[] varArgs = new String[]{"arg1", "arg2"};
        try{
            Method method = Test.class.getMethod("foo", String[].class);
            method.invoke(null, varArgs);

The first two calls will throw a java.lang.IllegalArgumentException: wrong number of arguments exception at runtime, while the latter two will invoke normally.

Reflection is obtained at runtime, and from the runtime perspective, variable-length parameters and arrays are identical, thus the method signature is:

// Method signature
([Ljava/lang/String;)V // public void foo(String[] varargs)

Now let's look at the method declaration of the Method object:

Object invoke(Object obj, Object... args)

Although args is a variable-length parameter, the length of args is constrained by the actual parameter list of the method represented by the method object. From the runtime signature, ([Ljava/lang/String;)V actually has only one formal parameter, which is String[] varargs, so in invoke(Object obj, Object… args), the actual length of the variable parameter args can only be 1.

// Object invoke(Object obj, Object... args)
// String[] varArgs = new String[]{"arg1", "arg2"};
method.invoke(null, varArgs); // varArgs length is 2, incorrect
method.invoke(null, (Object[])varArgs); // Converts String[] to Object[], length is 2, incorrect
method.invoke(null, (Object)varArgs); // Converts the entire String[] to Object, length is 1, correct
method.invoke(null, new Object[]{varArgs}); // Object[] length is 1, correct. The previous one and this one are equivalent

When to use variable-length parameters?

There is a question about the use of variable-length parameters on Stack Overflow. Simply put, new T[]{…}

>

Original article: https://blog.csdn.net/qiuchengjia/article/details/52910888

** Click me to share notes

Cancel

-

-

-

❮ Mysql Different Nnodb Myisam 9Patch 9 Remove Black Line ❯