2018年11月22日 星期四

Java SE 入門教學 - 可變動參數與陣列

更新時間:11/22/2018

前言

在寫方法時,我們有時候會使用到相同型別,但參數個數不同的時候,就會使用到多載(Overloading)或是傳入陣列解決問題。

當需要 1個、2個和3個相同型別參數時,使用多載可以解決;使用陣列在操作上就顯得有點麻煩。

public double average(int x){...};
public double average(int x1, int x2){...};
public double average(int x1, int x2, int x3){...};

當100個相同型別參數時,使用多載就必須要寫100個方法,萬一之後要修改,也要修改100個方法,這...太累人了!
所以,使用參數傳入陣列可以省事多了!只是每一次要使用前都要先建立陣列。

public double average(int[] x){...};

當如果要使用 1 ~ 20 個相同型別參數時,該使用多載還是使用陣列呢?這時可以考慮使用可變動參數的寫法,擁有相同型別但數量不同的參數。


一、可變動參數的規定

1.1) 只有用於方法的宣告
1.2) 宣告時若有多個參數,必須把可變動參數的語法宣告在最後一個參數。
1.3) 可變動參數的宣告,其功能與陣列相同。

宣告上的差異

public double average(int... x){...};
public double average(int[] x){...};

呼叫上的差異

double avg = average(10,20,30,40);

int[] p = new int{10,20,30,40};
double avg = average(p);

範例 1:操作可變動式參數


範例 2:可變動式參數換成一維陣列


範例 3:使用 int[]... ,可以傳入很多個一維陣列的可變動式參數

    int[]... 可以使用 int[][] 替換。



二、多個參數中使用變動式參數

2.1 宣告時若有多個參數,必須把可變動參數的語法宣告在最後一個參數,且只能使用一次變動式參數。

編譯錯誤 1:變動式參數不是在最後一個

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:變動式參數有兩個

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:如果能與固定和可變動參數的方法匹配時,優先使用固定參數的方法。


編譯錯誤 3:編譯器不知道該選哪一個方法

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。

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」而言,應該做如下的修改:

public static void main(String[] args){
  TestVarLenArg5 obj = new TestVarLenArg5();
  String[] strArr = null;
  obj.show("hello", strArr);
}

2.4 覆寫(Override)有變動式參數或陣列的方法時,不隨意改變參數形式

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 底下這個範例,是有陷阱的唷!您知道輸出結果是什麼嗎?



三、總結

變動式參數只有用於方法的宣告。

宣告時若有多個參數,必須把可變動參數的語法宣告在最後一個參數,且只能使用一次變動式參數。

避免帶有可變動參數的方法多載(Overloading),注意使用的原則。

別讓 null 值和空值威脅到可變動參數的方法,使用明確的參數型態,而不使用隱式。

覆寫(Override)有變動式參數或陣列的方法時,不隨意改變參數形式。

使用變動式參數時,其功能與陣列相同。





沒有留言:

張貼留言