2011-05-26 30 views
33

前段時間,甲骨文決定爲Java 8添加閉包將是一個好主意。我想知道如何解決設計問題與Scala相比,Scala從第一天起就已經關閉。Scala中的閉包vs Java中的閉包

援引開放的問題javac.info

  1. 能方法處理可用於功能類型? 如何做這項工作並不明顯。一個問題是,方法處理reify類型參數,但以一種干擾函數子類型的方式。

  2. 我們可以擺脫「拋出」類型參數的顯式聲明嗎? 只要聲明的邊界是一個檢查的異常類型,這個想法就是使用disjuntive類型推斷。這不是嚴格向後兼容的,但不太可能破壞真正的現有代碼。然而,由於語法模糊性,我們可能無法擺脫類型參數中的「拋出」。

  3. 禁止在舊式的循環索引變量

  4. 手柄接口,如比較定義一種以上的方法,所有,但其中一個@Shared將由Object繼承的方法來實現。 「使用單一方法的接口」的定義應該只計算在Object中不會實現的方法,並且應該將多個方法統計爲一個,如果實現其中一個方法將全部實現它們。主要的是,這需要更精確地說明接口只有一個抽象方法意味着什麼。

  5. 從指定函數類型映射接口:名稱,參數等 我們完全應該精確地指定由函數類型系統生成的接口的映射。

  6. 類型推斷。類型推斷的規則需要增加以適應異常類型參數的推斷。同樣,封閉轉換所使用的子類型關係也應該反映出來。

  7. 輔助異常類型參數,以幫助改進異常透明度。 也許make elided異常類型參數意味着界限。這樣可以通過添加一個新的通用異常參數來修改沒有異常類型參數的現有通用接口,如java.util.concurrent.Callable。

  8. 函數類型的類文字是如何形成的? 是否#void()。class?如果是這樣,如果對象類型被擦除,它是如何工作的?是嗎?(?)。class?

  9. 系統類加載器應動態生成函數類型接口。 函數類型對應的接口應該由引導類加載器按需生成,因此它們可以在所有用戶代碼之間共享。對於原型,我們可能會使用javac生成這些接口,以便原型生成的代碼可以在庫存(JDK5-6)VM上運行。

  10. 對lambda表達式的評估每次都必須產生一個新對象嗎? 希望沒有。例如,如果一個lambda從一個封閉範圍捕獲任何變量,它可以靜態分配。同樣,在其他情況下,如果lambda沒有捕獲循環內聲明的任何變量,則可以將其移出內部循環。因此,如果規範對於lambda表達式的結果的引用標識沒有任何承諾,那麼這樣的優化可以由編譯器完成。

據我瞭解2,6和7不是Scala中的一個問題,因爲Scala不使用檢查異常作爲某種「影子式系統」之類的Java。

其餘的呢?

回答

29

1)方法句柄可以用於函數類型嗎?

Scala面向沒有方法句柄的JDK 5和6,所以它還沒有試圖解決這個問題。

2)我們可以擺脫「throws」類型參數的顯式聲明嗎?

Scala沒有檢查異常。

3)不允許在舊式循環索引變量上使用@Shared。

Scala沒有循環索引變量。儘管如此,同樣的想法可以用某種類型的while循環來表達。 Scala的語義在這裏非常標準。捕獲符號綁定,並且如果符號恰好映射到可變參考單元,那麼就在您自己的頭上。

4)手柄等比較限定多於一個的方法,其中除一個來自對象

Scala的用戶往往使用的功能(或隱函數),來強迫正確類型的功能的接口接口。例如

[implicit] def toComparator[A](f : (A, A) => Int) = new Comparator[A] { 
    def compare(x : A, y : A) = f(x, y) 
} 

5)從函數類型指定映射到接口:

Scala的標準庫包括FuncitonN性狀0 < = N < = 22和規範說函數文本創建這些性狀的實例

6)類型推斷。類型推斷的規則需要增加以適應異常類型參數的推斷。

由於Scala沒有檢查過的異常可以在省略的異常類型的參數來幫助改進例外透明度這整個問題

7)踢。

同樣處理,沒有檢查的例外。

8)函數類型的類文字是如何形成的?它是否#void()。class?如果是這樣,如果對象類型被擦除,它是如何工作的?是嗎?(?)。class?

classOf[A => B] //or, equivalently, 
classOf[Function1[A,B]] 

類型擦除是類型擦除。上述文字產生scala.lang.Function1不管A和B的選擇如果願意,可以編寫

classOf[ _ => _ ] // or 
classOf[Function1[ _,_ ]] 

9)的系統,類裝載器應該動態地生成功能類型的接口。

Scala可以將參數個數限制在22以下,這樣它就不必動態生成FunctionN類。

10)每次都必須評估lambda表達式產生一個新對象嗎?

斯卡拉規範並沒有說它必須。但是從2.8.1開始,編譯器不會優化lambda不能從其環境中捕獲任何東西的情況。我還沒有用2.9.0測試過。

12

我只會在這裏解決數字4。

將Java「閉包」與其他語言中的閉包區分開來的一個原因是它們可以用來代替不描述函數的接口 - 例如Runnable。這就是SAM,單抽象方法的含義。

Java會這樣做是因爲這些接口在Java庫中比比皆是,並且它們在Java庫中比比皆是,因爲 Java是在沒有函數類型或閉包的情況下創建的。在他們缺席的情況下,需要反轉控制的每個代碼都必須使用SAM界面。

例如,Arrays.sort需要一個Comparator對象,該對象將執行要排序的數組成員之間的比較。相比之下,斯卡拉可以通過接收函數(A, A) => Int來對List[A]進行排序,該函數很容易通過閉包。但是,最後見注1。因此,因爲Scala的庫是爲帶有函數類型和閉包的語言而創建的,所以不需要像Scala中的SAM閉包那樣支持這樣的事情。

當然,還有一個Scala/Java互操作性的問題 - 而Scala的庫可能不需要類似SAM的庫,Java庫。有兩種方法可以解決。首先,因爲Scala支持閉包和函數類型,所以創建輔助方法非常容易。例如:

def runnable(f:() => Unit) = new Runnable { 
    def run() = f() 
} 

runnable {() => println("Hello") } // creates a Runnable 

其實,這個特殊的例子,可以通過使用Scala的按姓名參數進行更短,但是這不是重點。無論如何,這可以說是Java可以做的事情,而不是它將要做的事情。鑑於SAM接口的普及,這並不令人驚訝。

Scala處理此問題的另一種方式是通過隱式轉換。通過將implicit簡單地添加到runnable方法中,只要需要Runnable,但是提供了函數() => Unit,就會創建一個自動獲取(注2)的方法。

然而,牽涉是非常獨特的,並且在一定程度上仍然存在爭議。

注1:其實,這個特殊的例子是一些惡意選擇... Comparator有抽象方法,而不是一個,這是整個問題的。由於其中一種方法可以用另一種方法來實現,我認爲他們只是從抽象清單中「減去」防禦者方法。

而且,在斯卡拉的一面,即使是使用(A, A) => Boolean,不(A, A) => Int排序方法,排序方法的標準要求一個Ordering對象,這是相當類似於Java的Comparator!但在斯卡拉的情況下,Ordering扮演類型的角色。

注意2:一旦它們被導入到範圍中,將自動應用隱含,