有很好的记录,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)) }
在反编译的源代码中,我看到一个名为Function1的匿名子类型,它应该是我的关闭。但是apply()方法是空的,匿名类没有字段(可能存储闭包变量)。我想反编译器没有设法从类文件中获取有趣的部分…
现在问题:
解决方法
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 }
首先,我们来看看什么方法:
public int method(int); Code: 0: iload_1 1: aload_0 2: invokevirtual #17; //Method n:()I 5: iadd 6: ireturn
很简单这是一种方法。加载参数,调用n,add,return的getter。看起来就像Java。
功能怎么样?它实际上并没有关闭任何数据,但它是一个匿名函数(称为Close $$ anonfun $ function $ 1)。如果我们忽略任何专业化,构造函数和应用程序是最有意义的:
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
所以,你加载一个“this”指针并创建一个新的对象,它以封闭类作为参数。这是任何内部类的标准,真的。该函数不需要对外部类执行任何操作,因此只需调用super的构造函数即可。然后,当调用应用程序时,你会执行Box / unBox技巧,然后调用实际的数学 – 也就是添加5。
但是如果我们使用关闭中的变量关闭怎么办?安装程序是完全一样的,但是现在的构造函数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
也就是说,它检查以确保输入不为空(即外部类不为空)并将其保存在字段中。现在应用它的时候,拳击/拆箱包装后:
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
你看到它使用该字段引用父类,并为n调用getter。添加,返回,完成。所以,闭包很容易:匿名函数构造函数只是将封闭类保存在私有字段中。
现在,如果我们关闭不是内部变量,而是关闭方法参数呢?这就是Close $ anonfun $ mixed $ 1。首先,看一下混合方法的作用:
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
在调用构造函数之前加载参数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
其中该参数保存在私有字段中。由于我们不需要外部类,所以没有提及外部类。你应该不会感到惊讶:
public final int apply(int); Code: 0: iload_1 1: aload_0 2: getfield #18; //Field m$1:I 5: iadd 6: ireturn
是的,我们只是加载这个存储的字段并做我们的数学。
我不知道你在做什么,没有看到这个例子 – 对象有点棘手,因为他们有MyObject和MyObject $类,并且方法在两个之间分裂,可能不直观。但是应用绝对适用于事物,整个系统的工作原理与您期望的方式相似(在您坐下来,在很长一段时间内真的很难想像)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。