淺析scala傳名呼叫和傳值呼叫,: => 與() : =>
阿新 • • 發佈:2019-01-05
函式呼叫一般傳值呼叫,但是,在某些情況下,我們不希望函式的值首先被計算,而是等到呼叫的時候再來進行計算,為了適應這種情景,scala提供了傳名呼叫。
先來看兩個例子:
package test
/**
* Created by layne on 05/05/17.
*/
object Test {
def byValue(code: () => Unit): Unit = {
println("start")
code() //1
println("end")
}
def byName(code: => Unit): Unit = {
println("start" )
code
println("end")
}
def main(args: Array[String]): Unit = {
byValue {
//2
println("calculated")
() => println("test1")
}
println("------------------------")
byName {
println("uncalculated")
println("test2")
}
}
}
- 註釋1:這裡必須用code(),這裡函式值經過scala直譯器翻譯成了一個Function0的例項物件,而code存了這個物件的引用,實際上是code.apply()方法的呼叫
註釋2:這裡函式值將先被計算,計算結果恰好是一個簽名為 () => Unit 的匿名函式,如果計算結果為其他形式的話,這裡將不能編譯
執行結果為:
calculated
start
test1
end
------------------------
start
uncalculated
test2
end
這裡我們明顯看出來byValue中程式碼在傳入前就被計算了,而byName中的是在呼叫時才進行計算。
再來一個例子:
package layne.blog.byName
/**
* Created by layne on 08/05/17.
*/
object MyAssert {
def nameAssert(predicate: => Boolean, enableAssert: Boolean): Unit = {
if (enableAssert && !predicate) throw new Error("斷言錯誤")
}
def valueAssert(predicate: Boolean, enableAssert: Boolean): Unit = {
if (!enableAssert && predicate) throw new Error("斷言錯誤")
}
def main(args: Array[String]): Unit = {
//禁止斷言
//1
nameAssert(1 / 0 > 0, false) //禁止斷言的情況下,傳名呼叫不會報錯,因為predicate沒有被計算
//2
//valueAssert(1/0 > 0,false)
//允許斷言
//3
//nameAssert(1/0 >0 ,true)
}
}
依次執行上述函式結果
1:沒有任何輸出
2:Exception in thread "main" java.lang.ArithmeticException: / by zero
at layne.blog.byName.MyAssert$.main(MyAssert.scala:19)
at layne.blog.byName.MyAssert.main(MyAssert.scala)
3:Exception in thread "main" java.lang.ArithmeticException: / by zero
at layne.blog.byName.MyAssert$$anonfun$main$1.apply$mcZ$sp(MyAssert.scala:21)
at layne.blog.byName.MyAssert$.nameAssert(MyAssert.scala:9)
at layne.blog.byName.MyAssert$.main(MyAssert.scala:21)
at layne.blog.byName.MyAssert.main(MyAssert.scala)
2和3雖然都報了異常,但是請注意異常資訊的層級,同樣印證了nameAssert是在呼叫時進行計算的
最後來一個實際有用的例子:
package layne.blog.byName
/**
* Created by layne on 08/05/17.
*/
object TestUntil {
/**
* 與while作用相反
* @param condition
* @param block
*/
def until(condition: => Boolean)(block: => Unit): Unit = {
//執行
if (!condition) {
block
//傳名呼叫
until(condition)(block)
}
}
def main(args: Array[String]): Unit = {
var i = 0
//這裡利用柯里化和單引數呼叫能將()改成{},寫出形如內建關鍵字的程式碼
until(i > 4) {//直到i>4就停下來
println(i)
i += 1
}
}
}
最近在自學scala,順便嘗試寫寫部落格。希望給同樣在學的童鞋們一點幫助,也希望各位老鳥給點指導!