微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Scala闭包如何转换为Java对象?

我目前正在用不同的语言来看封闭实现。但是,当涉及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))
}

在反编译的源代码中,我看到一个名为Function1的匿名子类型,它应该是我的关闭。但是apply()方法是空的,匿名类没有字段(可能存储闭包变量)。我想反编译器没有设法从类文件获取有趣的部分…

现在问题:

>你知道转型是如何完成的?
>你知道哪里记录了吗?
>还有另一个想法我怎么能解决这个谜?

解决方法

让我们分开一个例子,以便我们看到它们有什么区别。 (如果使用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
}

首先,我们来看看什么方法

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 举报,一经查实,本站将立刻删除。

相关推荐