一連の例を分解して、それらがどのように異なるかを確認しましょう。(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
、add、returnします。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を呼び出すときに、ボックス/ボックス解除のトリックを実行してから、実際の数学を呼び出します。つまり、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
つまり、入力が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
そのフィールドを使用して親クラスを参照し、のゲッターを呼び出すことがわかります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
そのパラメータはプライベートフィールドに保存されます。外部クラスへの参照は必要ないため、保持されません。そして、あなたはどちらかを適用することによって驚くべきではありません:
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$
クラスがあり、メソッドは直感的ではないかもしれない方法で2つに分割されるため、オブジェクトは少しトリッキーです。しかし、applyは間違いなく適用され、システム全体は期待どおりに機能します(座って、非常に長い間非常に難しいことを考えた後)。