the*_*Dmi 18 closures decompiling scala
我目前正在研究不同语言的闭包实现.但是,当谈到Scala时,我无法找到关于如何将闭包映射到Java对象的任何文档.
有充分证据表明Scala函数映射到FunctionN对象.我假设对闭包的自由变量的引用必须存储在该函数对象的某处(例如,在C++ 0x中完成).
我还尝试使用scalac编译以下内容,然后使用JD反编译类文件:
object ClosureExample extends Application {
def addN(n: Int) = (a: Int) => a + n
var add5 = addN(5)
println(add5(20))
}
Run Code Online (Sandbox Code Playgroud)
在反编译的源代码中,我看到了一个匿名的Function1子类型,它应该是我的闭包.但是apply()方法是空的,并且匿名类没有字段(可能存储闭包变量).我想反编译器没有设法从类文件中获取有趣的部分......
现在问题:
Rex*_*err 31
让我们分解一组示例,以便我们看到它们之间的区别.(如果使用RC1,请编译-no-specialization以使事情更容易理解.)
class Close {
var n = 5
def method(i: Int) = i+n
def function = (i: Int) => i+5
def closure = (i: Int) => i+n
def mixed(m: Int) = (i: Int) => i+m
}
Run Code Online (Sandbox Code Playgroud)
首先,让我们看一下method:
public int method(int);
Code:
0: iload_1
1: aload_0
2: invokevirtual #17; //Method n:()I
5: iadd
6: ireturn
Run Code Online (Sandbox Code Playgroud)
非常直截了当.这是一种方法.加载参数,调用getter n,add,return.看起来就像Java.
怎么样function?它实际上并不关闭任何数据,但它是一个匿名函数(称为Close$$anonfun$function$1).如果我们忽略任何特化,那么构造函数和apply最感兴趣:
public scala.Function1 function();
Code:
0: new #34; //class Close$$anonfun$function$1
3: dup
4: aload_0
5: invokespecial #35; //Method Close$$anonfun$function$1."<init>":(LClose;)V
8: areturn
public Close$$anonfun$function$1(Close);
Code:
0: aload_0
1: invokespecial #43; //Method scala/runtime/AbstractFunction1."<init>":()V
4: return
public final java.lang.Object apply(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: invokestatic #26; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
5: invokevirtual #28; //Method apply:(I)I
8: invokestatic #32; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
11: areturn
public final int apply(int);
Code:
0: iload_1
1: iconst_5
2: iadd
3: ireturn
Run Code Online (Sandbox Code Playgroud)
因此,您加载一个"this"指针并创建一个新的对象,该对象将封闭的类作为其参数.这是任何内部类的标准,真的.该函数不需要对外部类做任何事情,所以它只调用super的构造函数.然后,当调用apply时,你执行box/unbox技巧,然后调用实际的数学 - 也就是说,只需添加5.
但是如果我们在Close中使用变量的闭包呢?安装程序完全相同,但现在构造函数Close$$anonfun$closure$1如下所示:
public Close$$anonfun$closure$1(Close);
Code:
0: aload_1
1: ifnonnull 12
4: new #48; //class java/lang/NullPointerException
7: dup
8: invokespecial #50; //Method java/lang/NullPointerException."<init>":()V
11: athrow
12: aload_0
13: aload_1
14: putfield #18; //Field $outer:LClose;
17: aload_0
18: invokespecial #53; //Method scala/runtime/AbstractFunction1."<init>":()V
21: return
Run Code Online (Sandbox Code Playgroud)
也就是说,它会检查以确保输入为非null(即外部类为非null)并将其保存在字段中.现在到了应用它的时候,在装箱/取消装箱包装之后:
public final int apply(int);
Code:
0: iload_1
1: aload_0
2: getfield #18; //Field $outer:LClose;
5: invokevirtual #24; //Method Close.n:()I
8: iadd
9: ireturn
Run Code Online (Sandbox Code Playgroud)
你看到它使用该字段来引用父类,并为其调用getter n.添加,返回,完成.因此,闭包很容易:匿名函数构造函数只是将封闭类保存在私有字段中.
现在,如果我们关闭不是内部变量,而是关闭方法参数呢?这是什么Close$$anonfun$mixed$1.首先,看看该mixed方法的作用:
public scala.Function1 mixed(int);
Code:
0: new #39; //class Close$$anonfun$mixed$1
3: dup
4: aload_0
5: iload_1
6: invokespecial #42; //Method Close$$anonfun$mixed$1."<init>":(LClose;I)V
9: areturn
Run Code Online (Sandbox Code Playgroud)
它m在调用构造函数之前加载参数!所以构造函数看起来像这样并不奇怪:
public Close$$anonfun$mixed$1(Close, int);
Code:
0: aload_0
1: iload_2
2: putfield #18; //Field m$1:I
5: aload_0
6: invokespecial #43; //Method scala/runtime/AbstractFunction1."<init>":()V
9: return
Run Code Online (Sandbox Code Playgroud)
其中该参数保存在私有字段中.不保留对外部类的引用,因为我们不需要它.申请时你也不应该感到惊讶:
public final int apply(int);
Code:
0: iload_1
1: aload_0
2: getfield #18; //Field m$1:I
5: iadd
6: ireturn
Run Code Online (Sandbox Code Playgroud)
是的,我们只是加载存储的字段并进行数学运算.
我不确定你做了什么,不能用你的例子看到这一点 - 对象有点棘手,因为它们有两个MyObject和MyObject$类,并且方法在两者之间以一种可能不直观的方式分开.但是应用绝对是适用的东西,整体而言,整个系统的工作方式与你期望的方式相同(在你坐下来思考它真的很难很长一段时间之后).
与Java的匿名内部类是伪闭包不同,并且不能修改看起来被封闭到其环境中的变量,Scala的闭包是真实的,因此闭包代码直接引用周围环境中的值.当从闭包中引用这些值时,这些值的编译方式不同,以使其成为可能(因为方法代码无法从当前激活帧以外的任何激活帧访问本地).
相反,在Java中,它们的值被复制到内部类中的字段,这就是语言需要封闭环境中的原始值的原因final,因此它们永远不会分歧.
因为所有Scala函数literal/closure对封闭环境中的值的引用都在函数文字apply()方法的代码中,所以它们不会显示为Function为函数文字生成的实际子类中的字段.
我不知道你是如何反编译的,但你如何这样做的细节可能解释了为什么你没有看到apply()方法体的任何代码.
| 归档时间: |
|
| 查看次数: |
1854 次 |
| 最近记录: |