Thinking in Java(4-6) 可變參數列表(Varargs)
Java 在 SE5 之後引入了可變參數列表(varargs)功能,使得方法能夠接收不定數量的參數,從而提供了更大的靈活性。本篇文章將探討 varargs 的使用方式,以及它與自動裝箱機制和方法多載的交互作用。
可變參數列表的基礎
在 Java 引入 varargs 之前,如果想要模擬 C 語言中的可變參數列表,需要透過建立 Object[]
陣列作為方法參數:
class A {}
public class VarArgs {
static void printArray(Object[] args) {
for(Object obj : args)
System.out.print(obj + " ");
System.out.println();
}
public static void main(String[] args) {
printArray(new Object[]{
new Integer(47), new Float(3.14), new Double(11.11)
});
printArray(new Object[]{"one", "two", "three" });
printArray(new Object[]{new A(), new A(), new A()});
}
} /* Output: (Sample)
47 3.14 11.11
one two three
A@1a46e30 A@3e25a5 A@19821f
*/
在上述程式中,printArray()
方法接受一個 Object[]
作為參數,透過 foreach
迴圈遍歷陣列中的元素。然而,這種方式需要在呼叫方法時顯式地建立陣列,語法較為繁瑣。
引入 varargs 的優勢
Java SE5 引入了 varargs 特性,允許方法接收不定數量的參數,而無需顯式地建立陣列。這使得方法的呼叫更為簡潔和直觀。
public class NewVarArgs {
static void printArray(Object... args) {
for(Object obj : args)
System.out.print(obj + " ");
System.out.println();
}
public static void main(String[] args) {
// Can take individual elements:
printArray(new Integer(47), new Float(3.14),
new Double(11.11));
printArray(47, 3.14F, 11.11);
printArray("one", "two", "three");
printArray(new A(), new A(), new A());
// Or an array:
printArray((Object[])new Integer[]{ 1, 2, 3, 4 });
printArray(); // Empty list is OK
}
} /* Output: (75% match)
47 3.14 11.11
47 3.14 11.11
one two three
A@1bab50a A@c3c749 A@150bd4d
1 2 3 4
*/
在這個範例中,printArray()
方法的參數宣告為 Object... args
,這表示該方法可以接受任意數量的 Object
型態參數。編譯器會自動將傳入的參數封裝成一個陣列,供方法內部使用。
可變參數列表的特性
傳遞零個或多個參數
可變參數列表允許我們傳遞零個或多個參數:
public class OptionalTrailingArguments {
static void f(int required, String... trailing) {
System.out.print("required: " + required + " ");
for(String s : trailing)
System.out.print(s + " ");
System.out.println();
}
public static void main(String[] args) {
f(1, "one");
f(2, "two", "three");
f(0);
}
} /* Output:
required: 1 one
required: 2 two three
required: 0
*/
在上述程式中,trailing
參數是可變參數列表,可以接收零個或多個 String
型態的參數。
使用非 Object 型態的可變參數列表
可變參數列表可以使用任何型態,包括基本型態:
public class VarargType {
static void f(Character... args) {
System.out.print(args.getClass());
System.out.println(" length " + args.length);
}
static void g(int... args) {
System.out.print(args.getClass());
System.out.println(" length " + args.length);
}
public static void main(String[] args) {
f('a');
f();
g(1);
g();
System.out.println("int[]: " + new int[0].getClass());
}
} /* Output:
class [Ljava.lang.Character; length 1
class [Ljava.lang.Character; length 0
class [I length 1
class [I length 0
int[]: class [I
*/
在這個例子中,f()
和 g()
方法分別接受 Character
和 int
型態的可變參數列表。
自動裝箱與可變參數列表
可變參數列表與自動裝箱(autoboxing)機制可以協同工作,使得基本型態與其包裝類型之間的轉換更加順暢:
public class AutoboxingVarargs {
public static void f(Integer... args) {
for(Integer i : args)
System.out.print(i + " ");
System.out.println();
}
public static void main(String[] args) {
f(new Integer(1), new Integer(2));
f(4, 5, 6, 7, 8, 9);
f(10, new Integer(11), 12);
}
} /* Output:
1 2
4 5 6 7 8 9
10 11 12
*/
在這個範例中,我們可以混合使用基本型態的數值和包裝類型的物件,編譯器會自動進行裝箱和拆箱操作。
可變參數列表與方法多載
可變參數列表的引入也對方法的多載(overloading)帶來了影響,需要注意可能的歧義問題:
public class OverloadingVarargs {
static void f(Character... args) {
System.out.print("first");
for(Character c : args)
System.out.print(" " + c);
System.out.println();
}
static void f(Integer... args) {
System.out.print("second");
for(Integer i : args)
System.out.print(" " + i);
System.out.println();
}
static void f(Long... args) {
System.out.println("third");
}
public static void main(String[] args) {
f('a', 'b', 'c');
f(1);
f(2, 1);
f(0);
f(0L);
//! f(); // Won't compile -- ambiguous
}
} /* Output:
first a b c
second 1
second 2 1
second 0
third
*/
在上述程式中,編譯器根據傳入參數的型態和數量,選擇最符合的多載方法。然而,當沒有傳遞參數時,編譯器無法確定應該調用哪一個方法,會導致編譯錯誤。
解決方法多載的歧義
為了解決上述的歧義問題,可以為方法增加非可變參數,讓編譯器能夠區分:
// {CompileTimeError} (Won't compile)
public class OverloadingVarargs2 {
static void f(float i, Character... args) {
System.out.println("first");
}
static void f(Character... args) {
System.out.print("second");
}
public static void main(String[] args) {
f(1, 'a');
f('a', 'b');
}
}
在這個範例中,兩個方法都有一個不同型態的非可變參數,編譯器可以根據傳入的參數型態進行正確的匹配。
結論
Java 的可變參數列表(varargs)為方法提供了更大的靈活性,允許接受不定數量的參數,並與自動裝箱機制協同工作。然而,在使用 varargs 與方法多載時,需要注意可能的歧義問題,透過增加非可變參數等方式來解決。
Comments ()