Scala传名参数调用时为何求值为String而非Lambda实例?
问题复现
以下是问题对应的可运行代码:
import scala.collection.mutable.ArrayBuffer object EntryPoint extends App { val arr = ArrayBuffer(1, 2, 3) doFirst(arr) def doFirst(a: ArrayBuffer[Int]) = { doSecond(s"$a") } def doSecond( x: => String = "" ) = { x match { case s: String => println(s"This is string: $s") case _ => println(x.getClass.getName) } } }
代码运行输出为:This is string: ArrayBuffer(1, 2, 3)
现象产生原因
该表现是Scala传名参数(by-name parameter)的标准设计导致的:
- 方法签名里的
x: => String不是普通的String类型入参,而是延迟执行的无参函数,对应实际类型为Function0[String]。你调试时看到的lambda实例,就是编译器自动把传入的s"$a"表达式包装生成的Function0实现类对象——这个对象在传入doSecond方法时不会立刻执行表达式逻辑,只是作为函数对象暂存。 - 调试时看到lambda实例显示带3个参数,是编译器为字符串插值
s"$a"生成实现逻辑的内部细节,不改变它作为Function0[String]无参函数的本质。
关于传名参数的引用逻辑
你猜测的「每次引用x本质是调用x.apply()」是完全准确的:
- 传名参数本身是Scala提供的语法糖,方法体内所有直接写
x的位置,编译器都会自动替换为对这个Function0实例的apply()方法调用,触发实际的表达式计算,最终得到String类型的结果。 - 你代码里的match逻辑在做
case s: String类型判断时,首先就触发了x的求值,拿到了拼接完成的String对象,因此直接命中String分支打印结果。如果想要获取x本身的lambda对象而不触发求值,需要先将它赋值给Function0类型的变量,例如写val f: () => String = x _,再对f做类型判断即可看到lambda的实际类型。
内容的提问来源于stack exchange,提问作者Igor Fedorov
相关产品推荐
相关产品推荐

