浅析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 的匿名函数,如果计算结果为其他形式的话,这里将不能编译

    执行结果为:

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)
    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,顺便尝试写写博客。希望给同样在学的童鞋们一点帮助,也希望各位老鸟给点指导!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值