scala - scala 中的闭包是如何实现的?

标签 scala closures decompiling

创建函数时,如何将函数范围之外的变量拉入函数中?我尝试反编译,但我无法理解它。看起来它使用了 putfield。 putfield 是否创建一个指向对象引用的指针?

最佳答案

答案是“视情况而定”。 scala 2.11 版本可能会对此进行一些重大更改。希望 2.11 能够内联简单的闭包。

但无论如何,让我们尝试给出当前 scala 版本的答案(下面的 javap 来自 scala 2.10.2)。下面是一个非常简单的闭包,它使用 val 和 var,以及生成的闭包类的 javap 输出。正如您所看到的,如果您捕获 var 或捕获 val,则存在主要区别

如果你捕获一个 val,它只会作为副本传递给闭包类(你可以这样做,因为它是一个 val)。

如果捕获 var,则必须在调用站点位置更改 var 本身的声明。 var 不再是位于堆栈上的本地 int,而是转换为 scala.runtime.IntRef 类型的对象。 。这基本上只是一个装箱整数,但具有可变的 int 字段。

(这有点类似于当您想要从匿名内部类内部更新字段时使用大小为 1 的最终数组的 java 方法)

这对性能有一些影响。当你在闭包中使用 var 时,你必须生成闭包对象以及 xxxRef 对象来包含 var。一件重要的事情是,如果您有这样的代码块:

var counter = 0
// some large loop that uses the counter

并在其他地方添加一个捕获计数器的闭包,循环的性能将显着降低。

所以底线是:捕获 val 通常没什么大不了的,但是捕获 var 时要非常小心

<小时/>
object ClosureTest extends App {
  def test() {
    val i = 3
    var j = 0
    val closure:() => Unit = () => {
      j = i
    }
    closure()
  }
  test()
}

这是生成的闭包类的javap代码:

public final class ClosureTest$$anonfun$1 extends scala.runtime.AbstractFunction0$mcV$sp implements scala.Serializable {
  public static final long serialVersionUID;

  public final void apply();
Code:
  0: aload_0       
  1: invokevirtual #24                 // Method apply$mcV$sp:()V
  4: return        

  public void apply$mcV$sp();
Code:
  0: aload_0       
  1: getfield      #28                 // Field j$1:Lscala/runtime/IntRef;
  4: aload_0       
  5: getfield      #30                 // Field i$1:I
  8: putfield      #35                 // Field scala/runtime/IntRef.elem:I
  11: return        

  public final java.lang.Object apply();
Code:
  0: aload_0       
  1: invokevirtual #38                 // Method apply:()V
  4: getstatic     #44                 // Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
  7: areturn       

  public ClosureTest$$anonfun$1(int, scala.runtime.IntRef);
Code:
  0: aload_0       
  1: iload_1       
  2: putfield      #30                 // Field i$1:I
  5: aload_0       
  6: aload_2       
  7: putfield      #28                 // Field j$1:Lscala/runtime/IntRef;
  10: aload_0       
  11: invokespecial #48                 // Method scala/runtime/AbstractFunction0$mcV$sp."<init>":()V
  14: return        
}

关于scala - scala 中的闭包是如何实现的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19806445/

相关文章:

scala - 关于用 JavaConverters 替换 JavaConversions

scala - Spark avro 到 Parquet

Groovy .each 闭包将元素包装在不需要的 Object[] 中

作为可选函数参数的 PHP 闭包

c# - 从 CLR 样式的类型全名获取 C# 样式的类型引用

java - "Closed"Java/Scala 中的 HashMap

web-applications - 用于 Lift Web 应用程序的 CouchDB 或 MongoDB?

closures - 闭包和柯里化(Currying)有什么区别和可能的相似之处?

在实际物理位置保存行号的java反编译器

c - HexRays - 输出读取了从未写入的值?