2017-05-06 5 views
4

私が知っているように、内部スコープと異種クラスは、生成されたバイトコードに格納されます(OuterClass $ 1.classなど)。 私は次の例の格納された変数を知っていただきたいと思います:λスコープの変数がラムダ式のために格納されるところ

public Function<Integer, Integer> curring(Integer elem) { 
    return x -> x * elem; 
} 

Arrays.asList(1, 2, 3, 4, 5).stream().map(curring(2)).map(curring(5)).forEach(System.out::println); 

ラムダはメソッドではなく、クラスに変換されます。これは、2つのコールが2つの別々のメソッドで生成されることを意味しますか?

+2

このトピックに関する興味深い読書を見つけました:[翻訳の訳](http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation。html) – Ving

+2

*面白い*読書ではない、それはラムダ翻訳についての読書です... – Eugene

答えて

7

この式x -> x * elem;は次のようになります静的方法に脱糖されようとしていますラムダはstateful lambdaと言われています。一方

は、あなたの map操作が java.util.Functionの実際のインスタンスを必要とする - それは一種のようになりますランタイムで を生成されます。

final class Test2$$Lambda$1 implements java.util.function.Function { 
     private final Integer arg$1; 

     private Test2$$Lambda$1(Integer arg$1){ 
      this.arg$1 = arg$1; 
     } 

     // static factory method 
     private static java.util.function.Function get$Lambda(Integer i){ 
      return new Test2$$Lambda$1(i); // instance of self 
     } 

     public Integer apply(Integer x) { 
      return YourClass.lambda$curring$0(this.arg$1, x);  
     } 
} 

Function.apply前(地図の操作内) と呼び、新しいインスタンスTest2$$Lambda$1であり、静的ファクトリメソッドget$Lambdaによって生成されます。これは、elem変数を「持ち歩く」必要があります。

マップの都度、が呼び出されるたびに、新しいインスタンスが作成されます

ストリームには5つの初期要素があるため、2つのmap操作では、10個のインスタンスが作成されます。

しかし、一般にこれは実装の詳細で、1日は簡単に変更される可能性があるため、頼りないでください。また、この短命のインスタンスの作成と収集(ガベージコレクタによる)は非常に安価で、ほとんどあなたのアプリには影響しません。

+0

ちょっとしたニックピッキング:生の 'Function'を実装するとき、' Object apply(Object x) 'が必要です。メソッドと型キャストこれは、実際に生成されたコードで現在実際に起こっていることです。さらに、 'this.i'は' this.arg $ 1'でなければなりません。キャプチャされた値が最初のパラメータ、関数パラメータ( 'x')が2番目のパラメータになります。生成された 'lambda curring $ 0'メソッドでは、' x'と 'y'ではなく、' elem'と 'x'を名前に使うのが混乱することはありません。 – Holger

+0

@Holger私の世界の中で。この答えは実際にあなたの*(このコメントの主なキーワードです)他の答えと多くのデバッグとたくさんのバイトコードを読んでいる実際には驚くべきことです...アドバイスのためのthx。 – Eugene

3

curring(..)メソッドを呼び出すたびに、新しいオブジェクトが作成されます。だから、あなたは2つのオブジェクトを持っています。

これは、ラムダがステートレスではなく、単独でしか動作しないためです。外部変数であるelemをキャプチャする必要があります。

public Function<Integer, Integer> curring() { 
    return x -> x * 2; 
} 

あなたのラムダはステートレスになります:2のような固定された数がelemではなく、あなたのラムダに使用された場合

。外部変数は取り込まれません。その場合、ラムダは複数回呼ばれるシングルトンになります。

この動作はJVMに依存し、上記はHotSpot JVMからのものであることに注意してください。あなたが内部elem変数capturingあるので

private static Integer lambda$curring$0(int x, int y){ 
     return x*y; 
    } 

:ここ

さらに詳しい情報:Does a lambda expression create an object on the heap every time it's executed?

関連する問題