java函數式編程之lambda表達式


作為比較老牌的面向對象的編程語言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的預覽版本,也可能正式版會做很多的改進,那就不得而知了。

相關文章:

Java 8為什么需要Lambda表達式

Java 8的Lambda表達式

說說Java 8中的Lambda表達式

還可以訪問我樹莓派上搭的博客地址:

http://www.codeforfun.info/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM