浅析scala传名调用和传值调用,: => 与() : =>

来源:互联网 发布:底层软件开发工程师 编辑:程序博客网 时间:2024/06/07 06:59
 函数调用一般传值调用,但是,在某些情况下,我们不希望函数的值首先被计算,而是等到调用的时候再来进行计算,为了适应这种情景,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 的匿名函数,如果计算结果为其他形式的话,这里将不能编译

    执行结果为:

calculatedstarttest1end------------------------startuncalculatedtest2end

这里我们明显看出来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)    23虽然都报了异常,但是请注意异常信息的层级,同样印证了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,顺便尝试写写博客。希望给同样在学的童鞋们一点帮助,也希望各位老鸟给点指导!

1 0