因此,在 C++ 中我是這麼理解的,在函數內部改變 call-by-value 傳入的參數並不會影響外部變數,因為傳進來的是它的複製品; 而改變 call-by-reference 傳入的參數則會影響到外部變數,因為 alias 是指向同一個 value 的。
基於這樣的理解,在學習 Java 的時候遇到「只有 call-by-value」這樣的觀念,便在讓我產生了疑惑:「要怎麼讓傳入函數的變數隨著函數內部對它操作而跟著變動」,也就是說 Java 要怎麼做到 call-by-reference 的語意呢?
後來發現,原本我以為 Java 的 call-by-value 和 C++ 的 call-by-value 有相同的立足點 (將物件複製一份丟進函數) 實際上是錯誤的。
原來,在 Java 當中,除了原生型別的變數,一般型別的變數其實不是直接 hold 物件本身,而是 hold 物件的 reference!
這點跟 C++ 就有一點出入了,在 C++ 中要用一個變數 hold 一個物件的 reference,只得明確用指標,像是 Dog* aDog = new Dog; 這樣一來,操作上也得用 -> 運算子,像是 aDog->setName('Lucky'); 如果是用 Dog aDog; 那 aDog 這個變數就是指物件本身(傳進函數會被複製一份)。
Java 沒有指標的觀念 (全物件導向),除了原生型別,它的物件都需要用 new 建立 (跟 C++ 的 new 又不太一樣,C++ new 是用來動態配置記憶體用的) 如 Dog aDog = new Dog; 這裡的 aDog 並不是 Dog 型別的物件本身,而是物件的一個 reference。
因此「要怎麼讓傳入函數的變數隨著函數內部對它操作而跟著變動」這個疑惑就解開了,Java 所謂的「只有 call-by-value」的 value 指的就是「物件的 reference」,既然物件的 reference 被複製一份進去函數,那當然可以透過函數內部操作該 reference 來改變外部的物件 (而且不需要用 -> 運算元)。
這裡又冒出一個問題,既然原生型別的物件在 Java 當中是直接 hold 物件的值,所以傳入函數參數的預設行為就是 call-by-value,那麼如果想要實現 call-by-reference 的語意該怎麼做呢?其實也很簡單,就是將該原生型別的變數包裝成一個陣列,再傳入函數即可。
public static void increment(int[] array, int amount) { array[0] = array[0] + amount; } public static void main(String args[]) { int[] myInt = { 1 }; increment (myInt, 5); System.out.println ("Array contents : " + myInt[0]); }
參考資料:
Java is Pass-by-Value, Dammit! - Scott Stanchfield
http://javadude.com/articles/passbyvalue.htm
Q&A : How do I pass a primitive data type by reference?
http://www.javacoffeebreak.com/faq/faq0066.html
... ...
No comments:
Post a Comment