如何在scala中实现闭包?
创建函数时,函数范围之外的变量是如何拉入函数的?我试过反编译,但理解起来有困难。它看起来像是使用了putfield。putfield是否生成指向对象引用的指针?让我们看一个具体示例:如何在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变量的引用以下是包含闭包的以下方法的字节码:
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
}