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

浅析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
    }
  }
}
发布了355 篇原创文章 · 获赞 84 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/qq_43193797/article/details/103629435