浅析scala传名调用和传值调用,: => 与() : =>
函数调用一般传值调用,但是,在某些情况下,我们不希望函数的值首先被计算,而是等到调用的时候再来进行计算,为了适应这种情景,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 的匿名函数,如果计算结果为其他形式的话,这里将不能编译
这里我们明显看出来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
}
}
}