標籤:
4. 副作用
在一些語言如Pascal中,子程式被分成兩種:函數和過程。雖然Java沒有強制性地要求將方法區分為命令和函數,然而這種區別對於良好地設計程式有很大的協助[1]。
首先說明一個概念:副作用(side effect)。副作用一般是針對操作(運算式)而言的,一個操作/運算式有“副作用”是指在對該運算式求值時,會改變程式的一個或多個資料,以致再次對該運算式求值時,可能會得出不同的結果。事實上,Java的4種運算式語句如賦值、自增自減、方法調用、對象建立都可能帶來副作用。
這裡討論方法的副作用。一個方法的執行,如果在返回一個值之外還導致某些外部“狀態”發生變化,則稱該方法產生了副作用。這裡所謂“狀態”發生變化,可以是執行個體域或靜態變數被修改、方法的實參被修改(Java 中不會出現這種情況。但是實參為引用時,其指向的對象可能被修改從而產生副作用)、將資料傳遞給顯示器、印表機或存入檔案中等等。
當然,方法內部的運算式也會出現副作用,如果它僅僅影響局部變數而不影響外部狀態,則方法沒有副作用。基於副作用概念,定義兩個術語:
2 有返回值而且沒有副作用的方法稱為函數(function)。
2 沒有返回值的方法必然有副作用,除非它的方法體是空的或者方法沒有意義。所以,沒有返回值的方法、有返回值但有副作用的方法稱為過程(procedure)或命令(command)。簡言之,有副作用的方法稱為過程。
如此嚴格地定義出函數的概念,是因為函數使得系統的狀態穩定,函數的行為容易預測。更進一步,如果函數是純粹的函數(pure function,純函數)——它的輸出值依靠和僅僅依靠其輸入、對於相同的輸入總是返回相同的值,(由於純函數的純粹和無副作用)對純函數的調用就能夠被一個值取代(或者說,將方法視為一個值),這就是函數式程式設計語言中著名的引用透明(referentialtransparency)特性。
常式 3?3純函數package semantics.method;public class SideEffectsDemo{ private static int x = 0; //純函數(pure function) public static int times(int i,int j){ return i * j ; } //非純函數 public static int m(int i,int j){ return i * j +x; }}
在函數式程式設計語言(functionalprogramming language)如Haskell[2]中,尤其強調避免副作用。當然了,完全不產生副作用的程式設計語言是沒有任何用處的,例如資料顯示和存入檔案等等副作用都是必要的,強調無副作用的Haskell 語言,使用一種技術將它們分離出來,用一種安全的方式單獨執行。
Java是命令式物件導向語言,但能夠借鑒函數式語言的優點,也期待Java中加入重要的函數式語言的特性。隨著Java 8的發布,引入的λ運算式(Lambda Expressions)表明,Java開始大力引入函數式語言的特性。(They enable you to treat functionality as a method argument, or code as data.)
練習3-5:何謂方法、函數、純函數? 解釋副作用的含義。 |
練習3-6:實現方法,求f(x)=x^3 + 3x+1。註:書中x^3表示x*x*x. |
[1] 參考RichardMitchell,Jim Mackim著,孟岩譯,Design by Contract原則與實踐。
[2] http://www.haskell.org/learning.html
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
《編程導論(Java)·3.1.2 方法》之 副作用