Scala方法的类型和方法作为参数

Man*_*anu 6 methods scala anonymous-function

在下面的代码示例中,我不明白为什么函数fun可以作为参数传递给方法addAction.该方法fun属于类型Unit,而该方法addAction需要类型的函数() => Unit.

如果fun是类型() => Unit,那么当我尝试添加到操作列表时,为什么编译器会抱怨fun类型?Unitfunactions = fun :: actions

package myscala

object MyScala {

  def fun() { println("fun1 executed.") }

  def addAction(a: () => Unit) {
    actions = a :: actions
  }

  var actions: List[() => Unit] = List()

  def main(args: Array[String]) {
    // the following line would produce a compiler error (found: Unit, required: () => Unit), it's OK
    // actions = fun :: actions
    actions = (() => fun) :: actions // OK
    // I would expect the same compiler error here (found: Unit, required: () => Unit), but it's OK why?
    addAction(fun)
    actions.foreach(_()) // prints twice "fun1 executed"
  }
}
Run Code Online (Sandbox Code Playgroud)

Tom*_*icz 8

以此作为介绍性示例:

def fun() { println("fun1 executed.") }

val a1 = fun
val a2: () => Unit = fun
Run Code Online (Sandbox Code Playgroud)

这两行编译和(由于类型推断)它们看起来相同.不过a1是类型Unit,而a2类型是() => Unit...这怎么可能?

由于您没有明确提供类型a1,因此编译器解释funfun类型的方法调用Unit,因此类型a1与类型相同fun.这也意味着这一行将打印fun1执行.

但是,a2已经明确声明了类型() => Unit.编译器在这里帮助你,它理解由于上下文需要一个类型的函数,() => Unit你提供了一个匹配这种类型的方法,它不应该调用该方法,而是将其视为一流函数!

您并不注定要明确指定类型a1.他说:

val a1 = fun _
Run Code Online (Sandbox Code Playgroud)

你现在明白你的问题在哪里吗?


Phi*_*ppe 5

您需要fun _在第一种情况下编写,以避免调用该方法并执行eta-expansion.

这将有效:

actions = (fun _) :: actions
Run Code Online (Sandbox Code Playgroud)

如果不这样做,则fun进行评估.

有关更多详细信息,请参见Scala语言参考的第6.7节(方法值).

至于为什么fun不在第二种情况下进行评估,这是因为类型推断可以清楚地得出结论,addAction期望一个函数.顺便说一下,类型fun是技术上的()Unit,而不是Unit方法类型,而不是值类型.请参阅第3.3.1节参考更多.

  • 我宁愿推荐更轻松的阅读:[Scala编程第9章](http://www.artima.com/pins1ed/control-abstraction.html) (3认同)