假如我們定義如下的函數:
(x:Int) => x + more
這里我們引入一個自由變量more.它不是所定義函數的參數,而這個變量定義在函數外面,比如:
var more =1
那么我們有如下的結果:
scala> var more =1 more: Int = 1 scala> val addMore = (x:Int) => x + more addMore: Int => Int = <function1> scala> addMore (100) res1: Int = 101
這樣定義的函數變量addMore 成為一個“閉包”,因為它引用到函數外面定義的變量,定義這個函數的過程是將這個自由變量捕獲而構成一個封閉的函數。有意思的是,當這個自由變量發生變化時,Scala的閉包能夠捕獲到這個變化,因此Scala的閉包捕獲的是變量本身而不是當時變量的值。
比如:
scala> more = 9999 more: Int = 9999 scala> addMore ( 10) res2: Int = 10009
同樣的,如果變量在閉包在發生變化,也會反映到函數外面定義的閉包的值。比如:
scala> val someNumbers = List ( -11, -10, -5, 0, 5, 10) someNumbers: List[Int] = List(-11, -10, -5, 0, 5, 10) scala> var sum =0 sum: Int = 0 scala> someNumbers.foreach ( sum += _) scala> sum res4: Int = -11
可以看到在閉包中修改sum的值,其結果還是傳遞到閉包的外面。
如果一個閉包所訪問的變量有幾個不同的版本,比如一個閉包使用了一個函數的局部變量(參數),然后這個函數調用很多次,那么所定義的閉包應該使用所引用的局部變量的哪個版本呢? 簡單的說,該閉包定義所引用的變量為定義該閉包時變量的值,也就是定義閉包時相當於保存了當時程序狀態的一個快照。比如我們定義下面一個函數閉包:
scala> def makeIncreaser(more:Int) = (x:Int) => x + more makeIncreaser: (more: Int)Int => Int scala> val inc1=makeIncreaser(1) inc1: Int => Int = <function1> scala> val inc9999=makeIncreaser(9999) inc9999: Int => Int = <function1> scala> inc1(10) res5: Int = 11 scala> inc9999(10) res6: Int = 10009
當你調用makeIncreaser(1)時,你創建了一個閉包,該閉包定義時more的值為1, 而調用makeIncreaser(9999)所創建的閉包的more的值為9999。此后你也無法修改已經返回的閉包的more的值。因此inc1始終為加一,而inc9999始終為加9999.