jdk8的新特性總結之lambda表達式

jdk8的lambda表達式和StreamAPI能夠簡化以前我們需要重複編寫的代碼,以前一直都是用jdk6,最近一個新項目開始使用jdk8,經過一段時間的使用,着實感受到了jdk8的強大便捷,本文對jdk8的新特性做了一個總結,希望可以幫助大家快速的瞭解並上手jdk8。

一、lambda表達式

lambda表達式讓我們可以將方法體作爲參數進行傳遞,最常見的是匿名內部類的實現,以前我們需要new一個對象,然後實現抽象方法,就像下面這樣:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("傳統new線程的方式");
    }
});

而我們用lambda表達式只需要一行代碼,將run方法的方法體傳遞給Thread即可:

Thread thread = new Thread(() -> System.out.println("hello lambda"));

這裏有一個疑問,這種寫法JDK是如何知道調用Thead的哪個構造器的呢?

首先lambda表達式傳遞是一行代碼,jdk會找一個只有一個參數,並且這個參數爲函數式接口的構造方法。來解釋一下什麼叫函數式接口。只有一個抽象方法的接口就叫做函數式接口,函數式接口可以用@FunctionalInterface來標識。而Runnable就是一個函數式接口。我們可以看一下Runable的源碼:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

總結:lambda表達式就是括號 + 箭頭 + 方法體。如果方法體只有一行代碼,則可以省略大括號,如果有返回值還可以省略return。lambda常見的格式:

  • (參數1, 參數2) -> 表達式
  • (參數1, 參數2) -> { 一系列表達式 }
  • 參數 -> 表達式

二、函數式接口

接下來我們來描述一個業務場景,通過這個業務場景來繼續瞭解一下函數式接口,假設有一個需求,傳入一個值,經過一系列計算之後返回一個值,這一系列計算是未知的,需要具體調用者去實現,代碼如下:

public class MyTest {
    @Test
    public void test(){
        //這裏爲了演示就直接定義內部類。
        int result1 = handle(10, new HandleInterface() {
            @Override
            public Integer handle(Integer i) {
                //...假裝有一系列業務操作
                return i * 100;
            }
        });
        System.out.println(result1);//輸出1000
        int result2 = handle(10, new HandleInterface() {
            @Override
            public Integer handle(Integer i) {
                //...假裝有一系列業務操作
                return i + 100;
            }
        });
        System.out.println(result2);  //輸出110
        //如同上面一樣,每一種業務操作,我們都會創建一個HandleInterface的實現類來實現具體的業務邏輯
    }

    /**
     * 處理方法,接受被處理的數字和HandleInterface的實現方法(具體業務邏輯)
     * @param num
     * @param h
     * @return
     */
    public int handle(Integer num, HandleInterface h){
        return h.handle(num);
    }
    /**
     * 一般從架構的考慮,我們都會定義一個抽象類,然後每一種計算(也可以說業務場景)我們都會定義一個類
     * 來實現它。
     * 因爲這個類只有一個抽象方法,所以我們也可以加上@Functionalinterface註解標識此接口爲函數式接口
     */
    @FunctionalInterface
    interface HandleInterface {
        public Integer handle(Integer i);
    }
}

而在jdk8中,已經在內部給我們定義好了許多的函數式接口,常用的有Suppiler(供給型),Comsumer(消費型),Function(函數型),Predicate(判斷型)。這些接口都是帶泛型的,有了這些內置的函數接口,我們就不需要自己定義接口了,接下來我們就用內置的函數式接口實現上線的功能:

public class MyTest2 {
    @Test
    public void test(){
        int result1 = handle(10, (e) -> e * 100); 
        System.out.println(result1);  //輸出1000
        int result2 = handle(10, (e) -> e + 100);
        System.out.println(result2);  //輸出110
    }
    /**
     * Function<T, R></>函數接口有兩個泛型, T表示參數類型,R表示返回值類型
     * @param num
     * @param f
     * @return
     */
    public Integer handle(Integer num, Function<Integer, Integer> f) {
        return f.apply(num);
    }
}

三、方法引用和構造器引用

 

總結一下:

  1. lambda表達式左邊的括號中可以寫入參,多個用逗號隔開,當只有一個參數時,小括號也可以不寫,並且可以省略參數類型,JDK會通過上下文進行類型推斷,推算出參數類型;
  2. lambda表達式的右邊寫具體的方法內容,如果只有一行代碼,則可以省略{}和return語句;
  3. 函數式接口表示只有一個抽象方法的接口,並且可以用@FunctionalInterface標識;
  4. Supplier<T>表示供給型接口,沒有入參,通過返回T類型結果,抽象方法:T get(),當
  5. Comsumer<T>表示消費型接口,有一個T類型的入參,沒有返回值,抽象方法:void accpet(T t),當你需要有兩個入參時,你可以使用它的子類BiConsumer<T, U>。
  6. Function<T, R>表示函數型接口,有一個T類型的入參,一個R類型的返回值,抽象方法:R apply(T t),同樣,當你有兩個入參時,你也可以使用它的子類,BiFunction<T, U, R>
  7. Predicate<T>表示判斷型接口,有一個T類型的入參,一個boolean類型的返回值,抽象方法:boolean test(T t),它也有多個入參類型的BiPredicate<T, U>
  8. 內置的函數型接口都放在java.util.function包下,裏面還有很多不帶泛型的接口,當你確定參數或返回值的數據類型時,可以使用這些函數接口。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章