Scala 的函数定义通常有两种,一种是
def f(x: R)
为传值调用(Call-by-value)。
另一种是
def f(x: => R)
为传名调用(Call-by-name)。
这两者的区别在于,传值调用在进入函数 f 之前,x 的值就已经被计算出来,而传名调用则会等到进入函数 f 内部,执行到有 x 参与的语句时,才计算 x 的值。
举例说明,在 Linux bash 命令行内运行如下命令创建 sbt project 的目录结构
mkdir test_call_project
cd test_call_project
mkdir -p src/{main,test}/scala
mkdir project target
echo 'name := "TestCallProject"
version := "1.0"
scalaVersion := "2.13.1"
scalacOptions := Seq("-unchecked", "-deprecation")' > build.sbt
创建 src/main/scala/TestCallByValueAndName.scala 文件,内容如下:
object TestCallByValueAndName {
def main(args: Array[String]) : Unit = {
println("call-by-name:");
call_by_name(x());
println("end call-by-name");
println("\n========\n");
println("call_by_value:");
call_by_value(x());
println("end call_by_value");
}
def x() : Long = {
println("计算 x 的值");
val y = 2L * 3L;
y
}
def call_by_name( y: => Long ) : Unit = {
println("在 call_by_name 方法内");
println("参数:" + y);
}
def call_by_value( y : Long ) : Unit = {
println("在 call_by_value 方法内");
println("参数:" + y);
}
}
在 test_call_project 目录下运行
sbt package
编译项目,待编译成功后,运行
sbt run
输出结果如下:
halo:test_call_project klcola$ sbt run
[info] Loading settings for project global-plugins from idea.sbt ...
[info] Loading global plugins from /Users/klcola/.sbt/1.0/plugins
[info] Loading project definition from /Users/klcola/IdeaProjects/learning_scala/test_call_project/project
[info] Loading settings for project test_call_project from build.sbt ...
[info] Set current project to TestCallProject (in build file:/Users/klcola/IdeaProjects/learning_scala/test_call_project/)
[info] Running TestCallByValueAndName
call-by-name:
在 call_by_name 方法内
计算 x 的值
参数:6
end call-by-name
========
call_by_value:
计算 x 的值
在 call_by_value 方法内
参数:6
end call_by_value
[success] Total time: 1 s, completed 2019-9-26 22:44:52
可见传名调用是在进入函数体 call_by_name 才开始函数 x 的计算,而传值调用则在进入函数体 call_by_value 之前就已经完成函数 x 的计算。