让我们分解一组示例,以便我们了解它们的不同之处。(如果使用 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
}
首先,让我们看看有什么method
作用:
public int method(int);
Code:
0: iload_1
1: aload_0
2: invokevirtual #17; //Method n:()I
5: iadd
6: ireturn
很简单。这是一种方法。加载参数,调用 getter n
,添加,返回。看起来就像Java。
怎么样function
?它实际上并没有关闭任何数据,但它是一个匿名函数(称为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”指针并创建一个以封闭类作为其参数的新对象。这是任何内部阶级的标准,真的。该函数不需要对外部类做任何事情,所以它只调用超级的构造函数。然后,在调用 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
也就是说,它检查以确保输入非空(即外部类非空)并将其保存在字段中。现在到了应用它的时候,在装箱/拆箱包装器之后:
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
. 添加,返回,完成。因此,闭包很简单:匿名函数构造函数只是将封闭类保存在私有字段中。
现在,如果我们关闭的不是内部变量,而是方法参数呢?就是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
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
该参数保存在私有字段中。没有保留对外部类的引用,因为我们不需要它。你也不应该对 apply 感到惊讶:
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$
类,并且方法以一种可能不直观的方式在两者之间拆分。但是 apply 肯定会应用事物,总体而言,整个系统几乎按照您期望的方式运行(在您坐下来认真思考很长时间之后)。