2017-10-08 23 views
1

如果我理解正確,在JVM下,每次使用lambda表達式時都必須創建一個Object。爲什麼Scala中的FunctionN類型被創建爲AnyRef的子類型,而不是AnyVal?

爲什麼開銷?爲什麼Scala創作者在設計FunctionN類型時選擇擴展AnyRef而不是AnyVal?我的意思是,它們本身沒有任何真正的'價值',所以函數不應該是具有基本單元表示的值對象(或者包含用於相等性檢查或等等的一些散列)的值?我可以想象不會爲每個lambda分配一個對象都會導致某些代碼庫中的性能提升。

我認爲擴展AnyVal的一個明顯的缺點是它會禁止子類化函數類型。也許這一點就足以成爲AnyVal的延伸,但還有什麼其他原因?

- 編輯

我明白功能需要關閉超過其他變量,但我認爲這將是更自然的將其建模作爲參數傳遞給應用方法,而不是功能N對象的字段成員(從而去除在這部分必須有一個java.lang.Object) - 畢竟,在編譯時不是所有已知的變量都關閉了嗎?

- 再次編輯

我發現了它;我想到的是'拉姆達升降'。

+3

功能確實具有「真正的價值」:他們關閉了。 –

回答

6

調用一個方法的唯一途徑是字節碼操作invokevirtual(階級虛擬調度),invokeinterface(相同,但在接口),invokespecial(調用正是給定的方法,忽略了虛擬查找。用於privatesupernew。)和invokestatic召喚獨角獸調用static方法)。 invokespecial出來了,因爲調用一些函數是抽象函數的對立面。 invokestatic也不在了,因爲它本質上是一個invokespecial克隆,它不需要參數thisinvokevirtualinvokeinterface與我們的目的相似足夠相似。

沒有辦法傳遞一個普通的函數指針,就像你在C中看到的那樣,即使你可以,也永遠無法調用它,因爲沒有可以跳轉到代碼中任意點的指令。 (方法內的所有跳轉目標都被限制在該方法中,並且所有對外部的引用歸結爲字符串(當然,JVM當文件加載後可以自由優化該表示)。)

In爲了用任一指令調用方法,JVM必須在目標對象的虛擬調度表內查找方法。如果您嘗試用()AnyVal)(中的子類不存在,但讓我們暫停我們的懷疑)試圖虛擬出對象,那麼當您嘗試調用一個(可能有趣的)方法的時候,JVM會非常困惑,到「無特徵的斑點」,你可以得到。

還要記住,一個對象的方法完全由它的類決定。如果a.getClass == b.getClass,那麼ab完全相同的方法,代碼和所有。爲了解決這個問題,我們需要創建FunctionN特徵的子類,以便每個子類代表一個函數,並且每個類的每個實例都包含對其類的引用,其中包含對與該函數關聯的代碼的引用。即使使用invokedynamic,這仍然是正確的,因爲LambdaMetaFactory的當前實現在運行時創建了一個內部類。

最後,@Oleg指出,函數不需要狀態的假設是錯誤的。閉包需要保持對其環境的引用,並且只有對象纔有可能。

相關問題