In Java 5, variable-length parameters were introduced, allowing an indefinite number of parameters to be passed when calling a method. Variable-length parameters are syntactic sugar in Java, essentially based on array implementation:
void foo(String... args); void foo(String[] args);
//方法签名 ([Ljava/lang/String;)V // public void foo(String[] args)
Defining Methods
When defining a method, add three dots after the last formal parameter…, indicating that this formal parameter can accept multiple parameter values, and the multiple parameter values are passed in as an array. There are several key points to note in the above definition:
Variable parameters can only be the last parameter of a function, but there may or may not be other parameters before them.
Since variable parameters must be the last parameter, a function can have at most one variable parameter.
Java variable parameters are transformed by the compiler into an array.
After variable-length parameters are compiled into bytecode, they appear in the method signature in array form. The signatures of these two methods are identical and cannot be used as method overloading. If they appear at the same time, the code cannot be compiled. Variable parameters are compatible with arrays, but the reverse is not true.
public void foo(String...varargs){} foo("arg1", "arg2", "arg3"); //上述过程和下面的调用是等价的 foo(new String[]{"arg1", "arg2", "arg3"});In J2SE 1.5, the"generics"mechanism was added, which can parameterize a type under certain conditions. For example, when writing a class, you can use an identifier (such as T) to represent the type of a method's formal parameter. What type this identifier actually represents is specified when generating an instance of the class. This mechanism can provide more sufficient code reuse and stricter compile-time type checking. However, the generics mechanism cannot be used together with variable-length formal parameters. If the type of a formal parameter that can match an indefinite number of actual arguments is represented by an identifier, the compiler will give an"generic array creation"error.
public class Varargs { public static void test(String... args) { for(String arg : args) {//当作数组用foreach遍历 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 Variable-Argument Methods
When calling a variable-argument method, you can pass zero to any number of arguments, and the compiler will convert the variable arguments into an array. You can also directly pass an array, as shown in the following example:
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个参数
test("a");//1个参数
test("a","b");//多个参数
test(new String[] {"a", "b", "c"});//直接传递数组
}
}
Method Overloading
Fixed Parameters Are Matched First
When calling an overloaded method, if the call can match both an overloaded method with fixed parameters and an overloaded method with variable-length parameters, the fixed-parameter method is selected:
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 优先匹配固定参数的重载方法
test();//version 1
}
}
> Matching Multiple Variable Arguments
When calling an overloaded method, if the call can match two overloaded methods with variable-length parameters, a compilation 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 Methods with Variable-Length Parameters
Even though the compiler can determine the specific method to call by preferring fixed parameters, readers of the code can still easily fall into traps. Overloading methods with variable-length parameters requires careful consideration.
Don't Let Null Values and Empty Values Threaten Variable-Length Methods
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 it as follows:
public static void main(String[] args) {
Client client = new Client();
String[] strs = null;
client.methodA("China",strs);
}
Letting the compiler know that this null value is of type String allows compilation to pass smoothly, thereby reducing the occurrence of errors.
Overriding Variable-Length Methods Must Also Follow the Rules
package com;
public class VarArgsTest2 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// 向上转型
Base base = new Sub();
base.print("hello");
// 不转型
Sub sub = new Sub();
sub.print("hello");//compile error
}
}
// 基类
class Base {
void print(String... args) {
System.out.println("Base......test");
}
}
// 子类,覆写父类方法
class Sub extends Base {
@Override
void print(String[] args) {
System.out.println("Sub......test");
}
}
The first one can compile successfully. Why is that? In fact, the base object upcasts the subclass object sub, and the parameter list is determined by the parent class, so it naturally passes. Now look at the case of direct invocation by the subclass. The compiler sees that the subclass has overridden the parent class's print method, so it will definitely use the print method redefined by the subclass. Even if the parameter list does not match, it won't go to the parent class to match again, because once it finds a match, it stops searching. Hence the type mismatch error.
This is a special case. The parameter list of an overriding method can actually be different from that of the parent class, which violates the definition of overriding and can cause inexplicable errors.
Here, let's summarize the conditions that overriding must satisfy:
An overriding method cannot narrow access permissions.
The parameter list must be the same as the overridden method (including the explicit form).
The return type must be the same as the overridden method, or a subclass of it.
An overriding method cannot throw new checked exceptions, or exceptions beyond the scope of the parent class, but it can throw fewer, more limited exceptions, or no exceptions.
Potential Problems
UsingObject…as a variable-length parameter:
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 toObject[], and is therefore treated as a plain array object;Integer[]can be cast toObject[], and can be used as an object array.
Precautions When Calling Methods via Reflection
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);
method.invoke(null, (Object[])varArgs);
method.invoke(null, (Object)varArgs);
method.invoke(null, new Object[]{varArgs});
} catch (Exception e){
e.printStackTrace();
}
}
}
Among the four calls above, the first two will throw a runtimejava.lang.IllegalArgumentException: wrong number of argumentsexception, while the last two are called normally.
Reflection is obtained at runtime. From a runtime perspective, variable-length parameters and arrays are identical, so the method signature is:
//方法签名 ([Ljava/lang/String;)V // public void foo(String[] varargs)
Let's also look at the method declaration of the Method object:
Object invoke(Object obj, Object... args)
Although args is a variable-length parameter, its length is limited by the length of the parameter list of the real method represented by the method object. From the runtime signature, ([Ljava/lang/String;)V actually has only one formal parameter, namely String[] varargs. Therefore, in invoke(Object obj, Object… args), the actual argument 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长度为2,错误
method.invoke(null, (Object[])varArgs); //将String[]转换为Object[],长度为2的,错误
method.invoke(null, (Object)varArgs);//将整个String[] 转为Object,长度为1,符合
method.invoke(null, new Object[]{varArgs});//Object[]长度为1,正确。上一个和这个是等价的
When to Use Variable-Length Arguments?
Stack OverflowThere is a question about using variable-length parameters. In short,
You can use variable-length parameters when you are not sure about the number of objects the method needs to process. This makes method calls simpler, without the need to manually create arrays.new T[]{…}
Original address: https://blog.csdn.net/qiuchengjia/article/details/52910888