如何在scala中实现闭包?

如何在scala中实现闭包?,scala,closures,decompiling,Scala,Closures,Decompiling,创建函数时,函数范围之外的变量是如何拉入函数的?我试过反编译,但理解起来有困难。它看起来像是使用了putfield。putfield是否生成指向对象引用的指针?让我们看一个具体示例: scala> var more = 1 more: Int = 1 scala> val f = (x: Int) => x + more f: Int => Int = <function1> 如您所见,闭包包含对捕获的more变量的引用以下是包含闭包的以下方法的字节码:

创建函数时,函数范围之外的变量是如何拉入函数的?我试过反编译,但理解起来有困难。它看起来像是使用了putfield。putfield是否生成指向对象引用的指针?

让我们看一个具体示例:

scala> var more = 1
more: Int = 1

scala> val f = (x: Int) => x + more
f: Int => Int = <function1>

如您所见,闭包包含对捕获的
more
变量的引用

以下是包含闭包的以下方法的字节码:

def run()
{
val buff = new ArrayBuffer[Int]();

val i = 7;

buff.foreach( a =>  {  a + i  }  )
}
字节码:

  public class com.anarsoft.plugin.scala.views.ClosureTest$$anonfun$run$1 extends   scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable {

// Field descriptor #14 J
public static final long serialVersionUID = 0L;

// Field descriptor #18 I
private final int i$1;

public ClosureTest$$anonfun$run$1(com.anarsoft.plugin.scala.views.ClosureTest $outer, int i$1);

...
编译器生成一个新的ClosureTest$$anonfun$run$1,其中包含一个构造函数,该构造函数包含两个范围外变量的字段,例如调用类的i和this。

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

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

如果捕获一个val,它将作为一个副本传递给closure类(因为它是val,所以可以这样做)

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

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

这对性能有一定影响。在闭包中使用变量时,必须生成闭包对象和xxxRef对象以包含变量。一个意思是,如果您有这样一块代码:

var counter = 0
// some large loop that uses the counter
并添加一个在其他地方捕获计数器的闭包,循环的性能将显著降低

因此,底线是:捕获VAL通常不是什么大事,但是在捕获VAR时要非常小心


下面是生成的闭包类的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        
}
public final类ClosureTest$$anonfun$1扩展scala.runtime.AbstractFunction0$mcV$sp实现scala.Serializable{
公共静态最终长SerialVersionId;
公共最终无效适用();
代码:
0:aload_0
1:invokevirtual#24//方法apply$mcV$sp:()V
4:返回
公开作废申请$mcV$sp();
代码:
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:返回
public final java.lang.Object apply();
代码:
0:aload_0
1:invokevirtual#38//方法apply:()V
4:getstatic#44//字段scala/runtime/BoxedUnit。单位:Lscala/runtime/BoxedUnit;
7:轮到你了
public ClosureTest$$anonfun$1(int,scala.runtime.IntRef);
代码:
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//方法scala/runtime/AbstractFunction0$mcV$sp.“”:()V
14:返回
}

“Scala捕获闭包之外的上下文”,这几乎不是一个解释……这似乎是对
object ClosureTest extends App {
  def test() {
    val i = 3
    var j = 0
    val closure:() => Unit = () => {
      j = i
    }
    closure()
  }
  test()
}
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        
}