在Scala中使用名称函数调用时,我遇到了一些意外行为。谁能解释一下这里的区别呢?
class Button( act: => Unit) {def fire = act}
def foo() {println("foo got called")}
val x= new Button(foo)
x.fire
val y= new Button(foo _)
y.fire
x.fire导致调用foo。 y.fire没有。 为什么? 传递给y的功能是什么? 谢谢!
答案 0 :(得分:4)
你应该对你的课程定义不同。
class Button( act: () => Unit) {def fire = act()}
请注意,您现在正在使用Function0[Unit]
而不是按名称Unit
取值。这个新定义有更好的类型,因为它需要一个函数,而你的接受任何值,除非有评估传入的值所需的副作用,否则什么也不做。我们现在也调用传入的函数来代替它的副作用回来吧。
Unit
的问题在于,当需要一个单位时,可以提供任何值,该值将自动为Unit
丢弃。例如,代码new Button(5)
有效,代码为val x: Unit = 5
。
第一个代码class Button( act: => Unit) {def fire = act}
“工作”的原因是,您调用方法foo,并将结果传递给Button
构造函数。因为它是一个名字参数foo
在它被使用之前实际上不会运行。然后,当您使用传递给类的Unit值时,需要对表达式求值,因此foo
很有趣。
您的第二个示例是不同的,但是添加下划线(foo _
)现在意味着您将方法本身作为函数传递,而不是像第一个示例中那样调用方法并传入结果。将函数传递给构造函数是完全正常的,即使现在的类型不是Unit
,因为如前所述,任何值都可以被丢弃并替换为Unit
。当你评估Unit值时,这次没有副作用,因为你没有运行方法来获取单位,只需创建一个在不运行的情况下被丢弃的函数。 def fire = act
将类型更改为函数时。
class Button( act: () => Unit) {def fire = act()}
现在只有() => Unit
是一个有效的参数而不是任何东西,而fire方法运行该函数。
答案 1 :(得分:3)
这是一个可能的解释!
scala> foo _
res14: () => Unit = <function0>
scala> foo
foo got called
scala>
你能明白为什么吗?
答案 2 :(得分:2)
由于Button
将(延迟的)Unit
作为构造函数参数,因此您认为不允许调用new Button(foo _)
,因为() => Unit
不是与=>Unit
相同。但事实证明,=> Unit
基本上是普遍接受者。
new Button()
new Button(99)
new Button("str")
你会得到警告,但这些都是编译和运行的,你可以在它们上调用fire
方法(但它不会做任何事情)。