Java8學習系列之匿名函數Lambda

Lambda介紹

Lambda,別名函數式編程,維基百科給出如下介紹:html

函數式編程是一種編程範式。它把計算當成是數學函數的求值,從而避免改變狀態和使用可變數據。它是一種聲明式的編程範式,經過表達式和聲明而不是語句來編程。java

Lambda表達式基於數學中的λ演算得名,直接對應於其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。Lambda表達式能夠表示閉包(注意和數學傳統意義上的不一樣)。編程

λ 演算是數理邏輯中的一個形式系統,在函數抽象和應用的基礎上,使用變量綁定和替換來表達計算。討論 λ 演算離不開形式化的表達。在本文中,咱們儘可能集中在與編程相關的基本概念上,而不拘泥於數學上的形式化表示。λ 演算其實是對前面提到的函數概念的簡化,方便以系統的方式來研究函數。api

Java中的Lambda

自Java8面世之後,也就表明着java今後之後一樣支持lambda語法,使得以前繁瑣的操做均可以使用簡便的語法進行代替,最具表明性的改革就是新增的Stream類,讓咱們對一個集合的排序、過濾、映射和採集更加方便!數組

咱們擬定一個場景,對於給定的一個int數組,過濾掉負數,並對剩餘的元素進行排序,在java8以前咱們的實現須要這麼寫:閉包

int[] array = {7, -2, 3, 5, -9, 3, -5, -1, 6, 8, 20};
List<Integer> list = new ArrayList<Integer>();
//過濾負數
for(int i: array) {
    if(i >= 0) list.add(i);
}
//排序
Collections.sort(list);

for(int i: list) {
    System.out.println(i);
}
複製代碼

使用Stream以後:app

int[] array = {7, -2, 3, 5, -9, 3, -5, -1, 6, 8, 20};
Arrays.stream(array)
    .filter(a -> a >= 0)    //過濾
    .sorted()               //排序
    .forEach(System.out::println);
複製代碼

能夠看到,實現的過程更加簡潔和優雅,lambda大大節省了代碼空間,提高了代碼可讀性,但使用的難度也隨之提升,對於傳統的編程方式,lambda語法無疑是一次重大的衝擊。ide

Java中Lambda語法的使用

函數式接口

什麼是函數式接口呢?在Java8以前,咱們想實現一個接口,最簡單的方式直接使用匿名類:函數式編程

Comparator<Integer> comparator = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 > o2 ? 1 : -1;
    }
};
複製代碼

這裏要注意,Comparator是一個接口類型,它的內部只有一個須要被實現的方法,那麼咱們將之稱之爲函數式接口,通常的函數式接口都會加上@FunctionalInterface註解,若是該接口待實現的方法超出兩個,你的IDE就會提醒你這不是一個規範的函數式接口,對於符合的,咱們就可使用lambda語法進行初始化:函數

Comparator<Integer> comparator = (o1, o2) -> o1 > o2 ? 1 : -1;
複製代碼

將之與java8以前的實現對比,咱們發現有不少共同之處,咱們來分析一下lambda的實現:

(o1, o2) -> o1 > o2 ? 1 : -1;
複製代碼

將上部分以->作分割線,分紅兩部分,它們分別是(o1, o2)o1 > o2 ? 1 : -1。很明顯,前者表明着函數的兩個入參,後者表明着兩個入參的邏輯實現,由此可得,lambda由兩部分組成:入參定義邏輯實現

對於一個函數式接口,咱們能夠用簡單的lambda語法去實現接口內惟一的待實現方法,反推一下,對於lambda這種匿名的函數定義風格,若是一個接口存在兩個待實現的方法,lambda則沒法具體表示實現的是哪個方法,由此反推可得,一個函數式接口最多隻能有一個待實現方法。

JDK對Lambda的支持

經過函數式接口的定義和lambda實現咱們知道了lambda語法的一個簡單格式,可是在開發過程當中,咱們不可能對於每個lambda的應用都定義個函數式接口,實際上,JDK中已經存在了不少lambda函數:

  • Function<T, R>:接受一個參數輸入,輸入類型爲 T,輸出類型爲 R。 抽象方法爲R apply(T)
  • BiFunction<T, U, R>:接受兩個參數輸入, T 和 U 分別是兩個參數的類型,R 是輸出類型。抽象方法爲R apply(T, U)
  • Consumer:接受一個輸入,沒有輸出。抽象方法爲 void accept(T t)
  • Predicate:接受一個輸入,輸出爲 boolean 類型。抽象方法爲 boolean test(T t)
  • Supplier:沒有輸入,一個輸出。抽象方法爲 T get()
  • BinaryOperator:接受兩個類型相同的輸入,輸出的類型與輸入相同,至關於 BiFunction<T,T,T>。
  • UnaryOperator:接受一個輸入,輸出的類型與輸入相同,至關於 Function<T, T>。
  • BiPredicate<T, U>:接受兩個輸入,輸出爲 boolean 類型。抽象方法爲 boolean test(T t, U u)。

它們分別應用於不一樣的場景,如下將會有幾個演示,首先使用lambda實現一個計算器:

BinaryOperator<Integer> cal = (a, b) -> a + b;
System.out.println(bo.apply(1, 2)); // 3
複製代碼

再來一個,使用lambda實現對數字正負的判斷

int a = 1;
int b = -1;
Predicate<Integer> predicate =  i -> i >= 0;
System.out.println(predicate.test(a));  //true
System.out.println(predicate.test(b));  //false
複製代碼

總結

在Stream中,lambda的應用很是普遍,咱們若是想講lambda更熟練的掌握,須要本身親自的去使用lambda,在實戰中去真正體會lambda的強大之處。

參考文章

相關文章
相關標籤/搜索