按名称调用: => 类型
=>类型
符号代表按名称调用,它是参数传递的众多方式之一。如果您不熟悉这些内容,我建议花些时间阅读维基百科文章,尽管现在通常使用的是按值调用和引用调用。
它意味着传递的值会被替换为函数内部的值名。例如,考虑以下函数:
def f(x: => Int) = x * x
如果我这样调用它
var y = 0
f { y += 1; y }
那么代码将会执行如下:
{ y += 1; y } * { y += 1; y }
虽然这引出了一个问题,即如果有标识符名称冲突会发生什么。在传统的按名调用中,一种叫做捕获避免替换的机制会发生,以避免命名冲突。然而,在Scala中,采用了另一种方式实现相同的结果--参数内的标识符名称不能引用或者遮盖被调用函数中的标识符。
介绍完其他两个相关点之后,还有一些与按名调用相关的内容需要讲解。
0元函数: () => Type
语法 () => Type
表示 Function0
类型的函数。也就是说,这是一个不带参数且返回某些值的函数。这类似于调用方法 size()
--它没有参数并返回一个数字。
有趣的是,这个语法与匿名函数字面量的语法非常相似,这导致有些人感到混淆。例如:
() => println("I'm an anonymous function")
是一个零元匿名函数字面量,其类型为
() => Unit
那么我们可以这样写:
val f: () => Unit = () => println("I'm an anonymous function")
重要的是不要混淆类型和值。
Unit => Type
实际上,这只是一个Function1
函数,它的第一个参数是Unit
类型。其他写法可能是(Unit) => Type
或者Function1[Unit, Type]
。然而,这很少是我们想要的。 Unit
类型的主要作用是指示一个不感兴趣的值,所以没有意义去接收该值。
例如,考虑以下情况:
def f(x: Unit) = ...
x
只能有一个值,因此不需要接收它,那么它可能有什么用呢?一种可能的用途是链式调用返回 Unit
的函数:
val f = (x: Unit) => println("I'm f")
val g = (x: Unit) => println("I'm g")
val h = f andThen g
由于andThen
只在Function1
上定义,而我们要链接的函数返回的是Unit
,因此我们必须将它们定义为Function1[Unit, Unit]
类型才能链接它们。
混淆的原因
第一个混淆的原因是认为0元函数类型和字面量之间的相似性也存在于按名称调用中。换句话说,认为因为:
() => { println("Hi!") }
是() => Unit
的文字表示,然后
{ println("Hi!") }
这里的=> Unit
不是字面量,而是一段代码块。
另一个令人困惑的地方是,Unit
类型的值写作()
,看起来像是一个0元参数列表(但实际上不是)。
case class Scheduled(time: Int)(callback: => Unit)
。这样做是可行的,因为第二个参数列表不会被公开暴露,也不会包含在生成的equals
/hashCode
方法中。 - nilskp