Scala允许像

def newCounter = {
  var a=0
  () => {a+=1;a}
}


它定义了一个函数,该函数在每次调用时均返回一个新的独立计数器函数,该函数从1开始:

scala> val counter1 = newCounter
counter1: () => Int = <function0>

scala> counter1()
res0: Int = 1

scala> counter1()
res1: Int = 2

scala> val counter2 = newCounter
counter2: () => Int = <function0>

scala> counter2()
res2: Int = 1

scala> counter1()
res3: Int = 3


这非常令人印象深刻,因为通常a代表newCounter堆栈帧上的内存地址。我刚刚阅读了“在Scala中编程”的闭篇章节,关于该问题,只有以下几句话(第155页):


在这种情况下,Scala编译器会重新安排事情,以便捕获的参数在堆(而不是堆栈)中有效,从而使创建它的方法调用的寿命更长。这种重新安排都是自动进行的,因此您不必担心。


谁能详细说明它在字节码级别上如何工作?访问是否类似于具有所有关联的同步和性能影响的类的成员变量?

最佳答案

您可以使用scalac -Xprint:lambdalift <scala-file-name>进行调查。

您的代码实际上是这样的:

def newCounter = {
  val a: runtime.IntRef = new runtime.IntRef(0);
  new Function0 {
    private[this] val a$1 = a
    def apply() = {
      a$1.elem = a$1.elem + 1
      a$1.elem
    }
  }
}


lambda使用的任何var都有一个包装。其他vars(未在闭包中使用)是公共语言环境变量。

到该包装器的链接在函数实例中存储为字段。

lambdalift中的-Xprint:lambdaliftcompiler phase。您可以使用-Xshow-phases获得所有阶段。您可以使用阶段号代替名称,当您不确定需要哪个阶段时,这很有用。

09-06 02:18