我们来看这个例子:
package scala
object ScalaTest3 {
def main(args : Array[String]) : Unit = {
runInThread(()=>println("Hi"))
Thread.sleep(5000)
println("Bye")
}
def runInThread(block:()=>Unit){
new Thread{
override def run(){
block()
}
}.start()
}
}
上例中,runInThread方法参数以类型为 ( ) => Unit 的函数的形式给出。不过,当你调用该函数时,需要写一段不那么美观的 ( ) => ,如上例:
runInThread(() => println("Hi"))
要想在调用中省掉 () =>,以及在参数声明中省略 ()[这像这样:block: => Unit,注意冒号与等号之间有空格],可以使用传名参数:
package scala
object ScalaTest3 {
def main(args : Array[String]) : Unit = {
runInThread(println("Hi")) //注意这里
Thread.sleep(5000)
println("Bye")
}
def runInThread(block: => Unit){ //注意这里
new Thread{
override def run {
block
}
}.start()
}
}
关于传名参数的定义,我们先来看看传值的参数与传名的参数的区别:
Scala的解释器在解析函数参数(function arguments)时有两种方式:先计算参数表达式的值(reduce the arguments),再应用到函数内部;或者是将未计算的参数表达式直接应用到函数内部。前者叫做传值调用(call-by-value),后者叫做传名调用(call-by-name)。
不过要使用传名参数,有两点要注意:1. 必须是无参函数作为另一个函数的参数。2. 作为参数的函数声明中必须省略 (),另外函数调用时必须省略 ()=>。见另一个例子:
package scala
object ScalaTest3 {
var assertionsEnabled = true
def main(args : Array[String]) : Unit = {
byNameAssert(5 > 3)
}
def byNameAssert(predicate: => Boolean) =
if(assertionsEnabled && !predicate) throw new AssertionError
}
使用传名参数的函数就是传名函数。要实现一个传名函数,要定义参数的类型开始于 => 而不是开始于 ()=>。如上例,可以通过改变其类型“() => Boolean”为“=>Boolean”,把myAssert的predicate参数改为传名参数。
我们再来看看传名函数的一个非常重要的特点:传名参数求值不会先于传名函数调用,这与按值传递不一样。下面我们先看一个按值传递的例子:
def boolAssert(predicate:Boolean) =
if(assertionsEnabled && !predicate) throw new AssertionError
这个例子与前一个例子之间存在一个非常重要的差别。因为boolAssert的参数类型是Boolean,在boolAssert(5>3)里,括号中的表达式先于boolAssert的调用被评估。表达式 5 > 3产生true,被传给boolAssert。相对的,因为byNameAssert的predicate参数的类型是 =>Boolean,byNameAssert(5 > 3)里括号中的表达式不是先于byNameAssert的调用被评估的。而是代之以先创建一个函数值,其apply方法将评估 5 > 3,而这个函数值将被传递给byNameAssert。