2017-05-06 74 views
4

正如我所知的內部和外部類的外部變量範圍存儲在生成的字節碼(例如OuterClass $ 1.class)中。 我想知道下一個例子,其中存儲的變量:超出範圍的變量存儲在lambda表達式中

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); 

LAMBDA被翻譯的方法,而不是類。這是否意味着這2個調用會產生2個獨立的方法?

+2

已經找到關於此主題的有趣閱讀:[lambda的翻譯(http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation。html) – Ving

+2

這不是一個有趣的*閱讀,那就是*閱讀關於lambda翻譯... – Eugene

回答

7

這種表達x -> x * elem;將被取消加糖的靜態方法,看起來像這樣:

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

因爲你是capturingelem變量拉姆達,拉姆達被認爲是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變量。

因爲那個每次調用地圖時都會創建一個新實例

由於您的Stream包含五個初始元素,因此將會創建10個實例 - 用於兩個map操作。

但是一般這是一個實現細節,可能很容易改變一天 - 所以不要依賴它。此外,這個短期實例的創建和收集(垃圾收集器)非常便宜,幾乎不會影響您的應用程序。

+0

有些挑剔:當你實現原始的'Function'時,你需要一個'Object apply(Object x)方法和類型轉換。這是生成的代碼中實際發生的事情。此外,'this.i'必須是'this.arg $ 1'。捕獲的值是第一個參數,函數參數('x')是第二個是正確的。在生成的'lambda $ curring $ 0'方法中,使用'elem'和'x'作爲這些參數的名稱,而不是'x'和'y',會更容易混淆...... – Holger

+0

@Holger,這是迄今爲止沒有挑剔的在我的世界裏。這個答案實際上是幾個小時的閱讀*你的*(這是這個評論中的主要關鍵字)其他答案和很多調試以及很多字節碼... thx的建議。 – Eugene

3

每次調用curring(..)方法時,都會創建一個新對象。所以是的,你會有2個對象。

這是因爲你的lambda不是無狀態的,也就是說它不能獨立工作。它需要捕獲一個外部變量,elem

如果您拉姆達使用像2一個固定的號碼,而不是elem

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

你拉姆達是無狀態的。它不會捕獲任何外部變量。在這種情況下,你的lambda將是一個多次調用的單例。

請注意,這種行爲是依賴於JVM的,而上述內容來自HotSpot JVM。

此處瞭解詳情:Does a lambda expression create an object on the heap every time it's executed?