作為比較老牌的面向對象的編程語言java,在對函數式編程的支持上一直不溫不火。
認為面向對象式編程就應該純粹的面向對象,於是經常看到這樣的寫法:如果你想寫一個方法,那么就必須把它放到一個類里面,然后new出來對象,對象調用這個方法。
這種方式在函數式編程語言看來太死板,沒有必要在對待多種編程范式上采取非此即彼的做法。
如今比較現代的編程語言也都是多編程范式的支持,不再去對一種編程范式固守一隅,一種語言可能會同時具有面向對象、函數式、元編程等多種特性,這方面java的后來者C#都走在她的前面。
終於在jdk8里發現了lambda表達式的影子,java也開始加入這種函數式編程特性,java碼農們終於在之前老土的方法之外有了一種更為簡便的選擇。
首先來看,lambda之前java的做法: 使用匿名內部類:
public void testAnonymousClass() { Integer[] nums = {2, 5, 1, 6}; Arrays.sort(nums, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { if(o1 < o2) return -1; return 0; } }); for (Integer n : nums) { System.out.println(n); } }
函數式編程語言的做法,這里拿go的代碼為例:
package main import ( "fmt" ) // 插入排序 func sort(nums []int, compare func (a, b int) int) { length := len(nums) for i := length - 1; i >= 0; i-- { for j := i; j + 1 < length; j++ { cur := nums[j] next := nums[j + 1] if compare(cur, next) > 0 { nums[j], nums[j + 1] = next, cur } } } } func main() { nums := []int{2, 5, 1, 6} sort(nums, func(a, b int) int { if a > b { return 1 } return 0 }) fmt.Println(nums) }
go的代碼看上去比較長,由於沒有像java一樣使用類庫提供的排序算法,所以go自己實現的插入排序。 這里go語言具有函數里面傳函數的能力(也叫高階函數),所以代碼看起來簡潔了很多。一般這種場景,函數式編程語言使用匿名函數的方式,在java的看來就必須通過匿名內部類來實現。首先實現一個接口,接口里面定義好方法,匿名內部類實現接口,然后在傳入的函數中,通過傳遞的對象,實現對匿名內部類里的方法的回調。這也就是lambda表達式之前的基本做法。
lambda表達式是對java實現函數式編程一個取巧方式的補充,下面來看lambda方式的做法:
public void testAnonymousClass() { Integer[] nums = {2, 5, 1, 6}; Arrays.sort(nums, (o1, o2) -> { if(o1 < o2) return -1; return 0; }); for (Integer n : nums) { System.out.println(n); } }
函數式接口:這是java在解決函數式編程,引入lambda表達式的同時引入的一個概念,具體的意思就是,定義的一個接口,接口里面必須有且只有一個方法,這樣的接口就成為函數式接口。 在可以使用lambda表達式的地方,方法聲明時必須包含一個函數式的接口。任何函數式接口都可以使用lambda表達式替換。 下面來看lambda的基本邏輯:
button.onAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { doSomethingWith(e); } });
使用lambda表達式替換:
button.onAction((ActionEvent e) -> { doSomethingWith(e); });
此lambda表達式的類型可由編譯器推斷為EventHandler,因為onAction()方法采用的對象類型為 EventHandler。 由於EventHandler只有一個方法即handle(),此lambda表達式必然是handle()方法的實現。 可以繼續簡化lambda表達式:
button.onAction((e) -> { doSomethingWith(e); });
此lambda表達式的參數必須是ActionEvent,因為其類型是由EventHandler接口的 handle()方法指定的。 因此,我們可以簡化此lambda表達式,因為其參數類型可以推斷。 還可以繼續簡化:
button.onAction(e -> doSomethingWith(e));
當lambda表達式只有一個參數且參數類型可以推斷時,則不需要括號。 lambda表達式中的代碼塊只包含一個語句,因此可以去掉大括號和分號。
可以猜測lambda表達式的實現可能是由java編譯器在編譯java字節碼時,會翻譯這樣的語法糖,最終還是轉化為匿名內部類來實現,至少從語義上看來是這樣的。那么它究竟怎樣做到的,這里的文章可以給出答案: 和Lambdas的第一次親密接觸
采用的辦法是在使用lambda表達式的類中生成一個實例方法,那么當然能夠訪問到這個類中定義的實例變量、靜態變量和公開、私有方法。 那和函數式編程相隨相生的閉包問題是否支持了呢? 通過上面的介紹可以看出java對函數式編程的實現,主要還是在編譯時對lambda表達式的一些轉化。 讓人看起來像是支持了匿名函數等函數式編程的特性,其實還是使用java自己的一套實現。所以在使用lambda表達式的時候最好頭腦清醒,不要糾結是否閉包了。 以上談的是jdk8的預覽版本,也可能正式版會做很多的改進,那就不得而知了。
相關文章:
還可以訪問我樹莓派上搭的博客地址: