1. 程式人生 > >《程式設計導論(Java)·3.1.2 方法》之 副作用

《程式設計導論(Java)·3.1.2 方法》之 副作用

4. 副作用

在一些語言如Pascal中,子程式被分成兩種:函式和過程。雖然Java沒有強制性地要求將方法區分為命令和函式,然而這種區別對於良好地設計程式有很大的幫助[1]

首先說明一個概念:副作用(side effect)。副作用一般是針對操作(表示式)而言的,一個操作/表示式有“副作用”是指在對該表示式求值時,會改變程式的一個或多個數據,以致再次對該表示式求值時,可能會得出不同的結果。事實上,Java的4種表示式語句如賦值、自增自減、方法呼叫、物件建立都可能帶來副作用。

這裡討論方法的副作用。一個方法的執行,如果在返回一個值之外還導致某些外部“狀態”發生變化,則稱該方法產生了副作用。這裡所謂“狀態”發生變化,可以是例項域或靜態變數被修改、方法的實參被修改(Java 中不會出現這種情況。但是實參為引用時,其指向的物件可能被修改從而產生副作用)、將資料傳遞給顯示器、印表機或存入檔案中等等。

當然,方法內部的表示式也會出現副作用,如果它僅僅影響區域性變數而不影響外部狀態,則方法沒有副作用。基於副作用概念,定義兩個術語:

²       有返回值而且沒有副作用的方法稱為函式(function)

²       沒有返回值的方法必然有副作用,除非它的方法體是空的或者方法沒有意義。所以,沒有返回值的方法、有返回值但有副作用的方法稱為過程(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