11-2 方法( Method )

前一個小節已大致介紹過如何定義方法,這個小節要再來看看如何在方法上定義參數、返回值,如何使用方法重載,並看看 JDK 5.0 中新增的不定長度引數( Variable-Length Argument )要如何使用。

• 定義參數與返回值

在 Java 中定義方法(Method)的基本語法如下所示:

[modifier] type identifier(type param, ...) {
    [return data;]
}
                            

其中:

  • modifier 是權限修飾,可以使用 publicprotectedprivate 來修飾,分別將方法修飾為公開的、保護的或私用的。
  • 第一個 type 是用來設定返回值型態,可以使用基本資料型態,也可以是自定義的物件型態,如果方法執行完畢後不返回任何資料,則設定為 void
  • identifier 是方法名稱。
  • ( 與 ) 之間的是參數列,用來設定方法可接受的引數個數與型態,每個參數之間以逗號區分,如果方法不接受任何引數,則參數列保持空白即可。
  • 從方法中返回值時使用 return 關鍵字並加上要返回的數值或變數,返回的數值或變數之型態必須與第一個 type 所設定的型態相同。

程式碼 11-1 的第 12 行到第 15 行示範過不返回值、不接受參數的 hello()方法:

public void hello() {
    System.out.print("Hello ");
    System.out.println(name);
}
                            

第 17 行到第 19 行則示範了不返回值、具有 String 型態參數的方法:

public void setName(String one) {
    name = one;
}
                            

參數列上的參數宣告,其作用與變數宣告相同,如果宣告的是基本型態參數,則呼叫方法時所給定的引數,會複製一份給參數;如果參數宣告時是物件型態,則宣告的參數作用是參考至物件,呼叫方法時所給定的物件,會被參數參考。

可以使用程式碼 11-3 來作個檢驗:

public class MethodDemo{
    public void primitiveParam(int para){
        para = 10;
    }

    public void referParam(int[] array){
        array[0] = 10;
    }

    public static void main(String[] args){
        int x = 5;
        int[] array = {1, 2, 3, 4, 5};
        MethodDemo demo = new MethodDemo();

        demo.primitiveParam(x);
        demo.referParam(array);

        System.out.println("x: " + x);
        System.out.print("array: ");

        for (int element : array){
            System.out.print(element);
            System.out.print(" ");
        }
        System.out.println();
    }
}
                            
程式碼 11-3 MethodDemo.java

您可以從圖 11-5 的執行結果看到,在呼叫 primitiveParam() 方法時,在方法中將 para 設定為 10,並不會影響 x 的值,這是因為 x 的值被複製一份給 para,而傳遞陣列物件時,參數 array 與 main 中的 array 參考的是同一物件,所以您在referParam()中改變索引 0 位置的元素,最後在 main 顯示出來的陣列元素也會受影響:

圖11-6 程式碼 11-3 的執行結果

程式碼 11-4 提供一個具有返回值,有兩個參數的方法示範,作用是利用輾轉相除法計算並傳回兩個數的最大公因數:

public class GCDFinder{
    public int gcdOf(int m, int n){
        int r;
        while (n != 0){
            r = m % n;
            m = n;
            n = r;
        }
        return m;
    }

    public static void main(String[] args){
        int a = Integer.parseInt(args[0]);
        int b = Integer.parseInt(args[1]);

        GCDFinder finder = new GCDFinder();
        System.out.println(a + ", " + b +
                " 最大公因數: " + finder.gcdOf(a, b));
    }
}
                            
程式碼 11-4 GCDFinder.java

程式碼中第 18 行會將 a、b 的值分別複製一份給 m 與 n,而返回值也是將 m 複製一份再返回,一個執行的結果範例如下所示:

圖11-7 程式碼 11-4 的執行結果

• 方法的重載( Overloading )

在定義方法時,有時候只是參數個數或是參數型態不同,但是方法的執行目的是一致的,這時候您可以為方法取相同的名稱,編譯器會自動依參數個數或型態來判斷您所呼叫的是哪一個方法。

舉個例子來說,在上一個單元中介紹過的 StringBuilder 類別,在進行字串附加的 append()方法,其實有多個版本:

圖11-8 StringBuilder 的 append()方法具有多個版本

像這樣依參數個數或引數型態不同,但允許方法具有同一個名稱的機制稱之為重載( Overloading ),要注意的是,實際呼叫方法時,您所給定的引數個數及引數型態,將作為判斷哪一個方法該被呼叫執行的依據。

要注意的是,返回值的型態並不作為方法重載的依據,所以類似於以下的程式碼片段是無法通過編譯的:

public int doSomething1() {
    return 1;
}
public double doSomething1() {
    return 1.0;
}
                            

• 不定長度引數

在 JDK 5.0 之前,參數的個數必須事先決定,然而有時您在設計方法時,並無法決定使用該方法的開發人員要傳遞多少引數給方法來使用,例如執行加總的 sumOf()方法,開發人員也許會希望它可以接受 3 個參數、4 個參數 5 個參數或更多的參數。

從 JDK 5.0 開始方法的參數設定上開始支援不定長度引數( Variable-Length Argument ),只要在宣告參數時,於型態指定後加上...即可,程式碼 11-5 直接以實例示範不定長度引數的使用。

public class SimpleAdder{
    public int sumOf(int... params){
        int sum = 0;
        for (int i = 0; i < params.length; i++){
            sum = sum + params[i];
        }
        return sum;
    }

    public static void main(String[] args){
        SimpleAdder adder = new SimpleAdder();
        System.out.print("1+2+3=");
        System.out.print("1+2+3+4=");
        System.out.print("1+2=");
        System.out.println(adder.sumOf(1, 2));
        System.out.println(adder.sumOf(1, 2, 3));
        System.out.println(
                adder.sumOf(1, 2, 3, 4));
    }
}
                            
程式碼 11-5 SimpleAdder.java

從程式碼中的第 4 行到第 6 行可以看到,被宣告的不定長度引數 params,實際上被轉換為陣列,這是在編譯時期由編譯器為您作的轉換動作,在 sumOf()方法中要使用陣列存取的方法來取得呼叫方法時所提供的引數。執行結果如下所示:

圖11-9 程式碼 11-5 的執行結果

重點提示

在 JDK 5.0 之前面對不定長度引數的需求時,使用陣列先收集好引數,再將整個陣列傳遞給方法,也正是解決需求的方案之一。

在方法上設定不定長度引數時,記得必須設定在引數列的最後一個,例如下面的方式是合法的:

public void someMethod(
        int arg1, int arg2, int... varargs) {
    // ....
}
                            

但下面的方式是不合法的:

public void someMethod(
        int... varargs, int arg1, int arg2) {
    // ....
}
                            

您也沒辦法設定兩個以上的不定長度引數,理由很簡單,編譯器無法決定引數的長度各是為何,例如下面的方式是不合法的:

public void someMethod(
        int... varargs1, int... varargs2) {
    // ....
}