前言
在寫方法時,我們有時候會使用到相同型別,但參數個數不同的時候,就會使用到多載(Overloading)或是傳入陣列解決問題。
當需要 1個、2個和3個相同型別參數時,使用多載可以解決;使用陣列在操作上就顯得有點麻煩。
public double average(int x1, int x2){...};
public double average(int x1, int x2, int x3){...};
當100個相同型別參數時,使用多載就必須要寫100個方法,萬一之後要修改,也要修改100個方法,這...太累人了!
所以,使用參數傳入陣列可以省事多了!只是每一次要使用前都要先建立陣列。
當如果要使用 1 ~ 20 個相同型別參數時,該使用多載還是使用陣列呢?這時可以考慮使用可變動參數的寫法,擁有相同型別但數量不同的參數。
一、可變動參數的規定
1.1) | 只有用於方法的宣告 |
1.2) | 宣告時若有多個參數,必須把可變動參數的語法宣告在最後一個參數。 |
1.3) | 可變動參數的宣告,其功能與陣列相同。 |
宣告上的差異
public double average(int[] x){...};
呼叫上的差異
int[] p = new int{10,20,30,40};
double avg = average(p);
範例 1:操作可變動式參數
xxxxxxxxxx
class MyAverage {
public double avg(int... x){
double sum = 0;
for(int tmp : x){
sum += tmp;
}
return sum/x.length;
}
}
class TestMyAverage {
public static void main(String[] args){
MyAverage obj = new MyAverage();
System.out.println("13+12+15平均值:" + obj.avg(13,12,15));
System.out.println("18+27+32+15+10平均值:" + obj.avg(18,27,32,15,10));
}
}

範例 2:可變動式參數換成一維陣列
xxxxxxxxxx
class MyAverage2 {
public double avg(int[] x){
double sum = 0;
for(int tmp : x){
sum += tmp;
}
return sum/x.length;
}
}
class TestMyAverage2 {
public static void main(String[] args){
MyAverage obj = new MyAverage();
int[] p = new int[]{13, 12, 15};
System.out.println("13+12+15平均值:" + obj.avg(p));
p = new int[]{18, 27, 32, 15, 10};
System.out.println("18+27+32+15+10平均值:" + obj.avg(p));
}
}

範例 3:使用 int[]... ,可以傳入很多個一維陣列的可變動式參數
int[]... 可以使用 int[][] 替換。
xxxxxxxxxx
class ConcatFirst {
public String show(int[]... x) {
String str = "";
for(int[] tmp : x){
str += tmp[0];
}
return str;
}
}
class TestConcatFirst {
public static void main(String[] args){
ConcatFirst obj = new ConcatFirst();
int[] p1 = new int[]{10, 20};
int[] p2 = new int[]{30, 40, 50};
int[] p3 = new int[]{60, 70, 80, 90};
System.out.println(obj.show(p1, p2, p3));
}
}

二、多個參數中使用變動式參數
2.1 宣告時若有多個參數,必須把可變動參數的語法宣告在最後一個參數,且只能使用一次變動式參數。
編譯錯誤 1:變動式參數不是在最後一個
xxxxxxxxxx
class TestVarLenArg1 {
public static void main(String[] args){
}
public void show(int... x, String str){
}
}
TestVarLenArg1.java:6: error: ')' expected public void show(int... x, String str){ ^ TestVarLenArg1.java:6: error: ';' expected public void show(int... x, String str){ ^ 2 errors
編譯錯誤 2:變動式參數有兩個
xxxxxxxxxx
class TestVarLenArg2 {
public static void main(String[] args){
}
public void show(int... x, String... str){
}
}
TestVarLenArg2.java:6: error: ')' expected public void show(int... x, String... str){ ^ TestVarLenArg2.java:6: error: <identifier> expected public void show(int... x, String... str){ ^ TestVarLenArg2.java:6: error: <identifier> expected public void show(int... x, String... str){ ^ 3 errors
2.2 避免帶有可變動參數的方法多載(Overloading)
成功執行 1:如果能與固定和可變動參數的方法匹配時,優先使用固定參數的方法。
xxxxxxxxxx
class TestVarLenArg3 {
public static void main(String[] args){
TestVarLenArg3 obj = new TestVarLenArg3();
obj.show("hello");
obj.show("hello", null);
}
public void show(String test){
System.out.println("----------");
}
public void show(String... str){
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
}
}

編譯錯誤 3:編譯器不知道該選哪一個方法
xxxxxxxxxx
class TestVarLenArg4 {
public static void main(String[] args){
TestVarLenArg4 obj = new TestVarLenArg4();
obj.show("hello");
obj.show("hello", "Ethan");
}
public void show(String test, String... str){
System.out.println("----------");
}
public void show(String... str){
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
}
}
TestVarLenArg4.java:4: error: reference to show is ambiguous obj.show("hello"); ^ both method show(String,String...) in TestVarLenArg4 and method show(String...) in TestVarLenArg4 match TestVarLenArg4.java:5: error: reference to show is ambiguous obj.show("hello", "Ethan"); ^ both method show(String,String...) in TestVarLenArg4 and method show(String...) in TestVarLenArg4 match 2 errors
2.3 別讓 null 值和空值威脅到可變動參數的方法
編譯錯誤 4:只有一個參數的方法,相當於後方都是空值;String 與 Integer 物件初始值都可以是 null。
xxxxxxxxxx
class TestVarLenArg5 {
public static void main(String[] args){
TestVarLenArg5 obj = new TestVarLenArg5();
obj.show("hello");
obj.show("hello", null);
}
public void show(String test, String... str){
}
public void show(String test, Integer... is){
}
}
TestVarLenArg5.java:4: error: reference to show is ambiguous obj.show("hello"); ^ both method show(String,String...) in TestVarLenArg5 and method show(String,Integer...) in TestVarLenArg5 match TestVarLenArg5.java:5: error: reference to show is ambiguous obj.show("hello", null); ^ both method show(String,String...) in TestVarLenArg5 and method show(String,Integer...) in TestVarLenArg5 match TestVarLenArg5.java:5: warning: non-varargs call of varargs method with inexact argument type for last parameter; obj.show("hello", null); ^ cast to String for a varargs call cast to String[] for a non-varargs call and to suppress this warning 2 errors 1 warning
這裡隱藏了實際參數類型,這是很不好的編碼習慣。不僅僅調用者需要"猜測"該調用哪個方法,而且被調用者也可能產生內部邏輯混亂。對於「編譯錯誤 4」而言,應該做如下的修改:
TestVarLenArg5 obj = new TestVarLenArg5();
String[] strArr = null;
obj.show("hello", strArr);
}
2.4 覆寫(Override)有變動式參數或陣列的方法時,不隨意改變參數形式
xxxxxxxxxx
// 基底類別
class Base {
public void show(String... args) {
System.out.println("Base......test");
}
}
// 子類別,覆寫父類別方法
class Sub extends Base {
public void show(String[] args) {
System.out.println("Sub......test");
}
}
class TestVarLenArg6 {
public static void main(String[] args){
// 向上轉型
Base base = new Sub();
base.show("hello");
// 不轉型
Sub sub = new Sub();
sub.show("hello");
}
}
TestVarLenArg6.java:24: error: show(String...) in Base is defined in an inaccessible class or interface sub.show("hello"); ^ 1 error
第一個能編譯通過,這是為什麼呢?事實上,base 參考物件變數把子類別物件實例 new Sub() 做了向上轉型,參考物件變數是由父類別決定的,當然能通過。而看看子類別直接調用的情況,這時編譯器看到子類別覆寫了父類別的 show() 方法,因此肯定使用子類別重新定義的 show() 方法,儘管參數不匹配也無法訪問父類別的方法。
2.5 底下這個範例,是有陷阱的唷!您知道輸出結果是什麼嗎?
xxxxxxxxxx
class TestVarLenArg7 {
public static void m1(String s, String... ss) {
for (int i = 0; i < ss.length; i++) {
System.out.println(ss[i]);
}
}
public static void main(String[] args) {
m1("");
m1("aaa");
m1("aaa", "bbb");
}
}
三、總結
變動式參數只有用於方法的宣告。
宣告時若有多個參數,必須把可變動參數的語法宣告在最後一個參數,且只能使用一次變動式參數。
避免帶有可變動參數的方法多載(Overloading),注意使用的原則。
別讓 null 值和空值威脅到可變動參數的方法,使用明確的參數型態,而不使用隱式。
覆寫(Override)有變動式參數或陣列的方法時,不隨意改變參數形式。
使用變動式參數時,其功能與陣列相同。
沒有留言:
張貼留言