2015-03-24 50 views
2

有鑑於此:Lambda表達式VS方法參照實施細則

class MyClass { 
    static class A { 
     public boolean property() { 
      return Math.random() < 0.5; 
     } 
    } 

    static List<A> filterLambda(List<A> list) { 
     return list.stream().filter(a -> a.property()).collect(Collectors.toList()); 
    } 

    static List<A> filterMethodCall(List<A> list) { 
     return list.stream().filter(A::property).collect(Collectors.toList()); 
    } 
} 
  • 什麼是什麼編譯器每種方法在區別在哪裏?
  • 如果有任何問題,內存使用情況或運行時是否有差異? (即使它很小,問題只是學術問題)

PD:我知道這個問題與this one類似,但我認爲它沒有得到正確解決。

+1

「編譯器做什麼?」可能是超出了這裏的答案的範圍。該規範提供了一些理論背景信息,但不清楚你想深入實際編譯器*實現*的深度。我可以肯定地說**是爲兩種方法生成的字節碼都是相同的**。 – Marco13 2015-03-25 15:04:45

回答

4

這是從Brian Goetz's doc的提取物由Brett奧肯聯:

當編譯器遇到一個lambda表達式,它首先降低 (desugars)拉姆達身體成方法其參數列表和 返回類型與lambda表達式的匹配,可能還有一些 附加參數(對於從詞法作用域捕獲的值,如果有 任何。)在將捕獲lambda表達式的位置, 它會生成一個invokedynamic調用站點, ,當被調用時,返回 lambda正在被轉換的功能接口實例 。該呼叫站點稱爲lambda工廠,用於給定 lambda。 lambda工廠的動態參數是從詞法作用域捕獲的值 。 lambda 工廠的引導方法是Java語言運行時庫中的標準化方法, 稱爲lambda元數據集。靜態引導參數在編譯時捕獲關於lambda的已知信息(它將被轉換到的接口的功能性 接口,用於 desugared lambda體的方法句柄,關於SAM類型是否是 可序列化等的信息)

方法引用的處理方式一樣的lambda表達式, 除了大多數方法引用並不需要被脫到 新方法;我們可以簡單地爲 引用的方法加載一個常量方法句柄,並將其傳遞給該元數據。從同一文檔中提取

實例:

作爲一個例子,考慮一個捕獲場minSize屬性拉姆達:

list.filter(e -> e.getSize() < minSize) 

我們desugar此作爲一個實例方法,並通過接收器作爲第一捕獲參數:

list.forEach(INDY((MH(metaFactory), MH(invokeVirtual Predicate.apply), 
        MH(invokeVirtual B.lambda$1))(this)))); 

private boolean lambda$1(Element e) { 
    return e.getSize() < minSize; } 

雖然

list.filter(String::isEmpty) 

被翻譯爲:

list.filter(indy(MH(metaFactory), MH(invokeVirtual Predicate.apply), 
      MH(invokeVirtual String.isEmpty))())) 
1

Here是關於方法參考的java語言規範。

Here是lambda表達式的規範。

從規則的角度來看,蘭姆巴斯比較複雜。

但是,在這兩種情況下,結果都是invokedynamic調用。

Brian Goetz寫了一個關於如何工作的doc

+0

我不是問哪一個應該使用,但感謝鏈接 – hithwen 2015-03-24 23:46:53