從java8出現以來lambda是最重要的特性之一, 它可以讓我們用簡潔流暢的代碼完成一個功能。 很長一段時間java被吐槽是冗餘和缺乏函數式程式設計能力的語言, 隨著函數式程式設計的流行java8種也引入了 這種程式設計風格。 在此之前我們都在寫匿名內部類幹這些事, 但有時候這不是好的做法, 本文中將介紹和使用lambda, 帶你體驗函數式程式設計的魔力。
什麼是lambda?lambda運算式是一段可以傳遞的代碼, 它的核心思想是將物件導向中的傳遞資料變成傳遞行為。 我們回顧一下在使用java8之前要做的事, 之前我們編寫一個執行緒時是這樣的:
也有人會寫一個類去實現Runnable介面, 這樣做沒有問題, 我們注意這個介面中只有一個run方法, 當把Runnable物件給Thread物件作為構造參數時創建一個執行緒, 運行後將輸出do something.。 我們使用匿名內部類的方式實現了該方法。
這實際上是一個代碼即資料的例子, 在run方法中是執行緒要執行的一個任務, 但上面的代碼中任務內容已經被規定死了。 當我們有多個不同的任務時, 需要重複編寫如上代碼。
設計匿名內部類的目的, 就是為了方便 Java 程式師將代碼作為資料傳遞。 不過, 匿名內部 類還是不夠簡便。 為了執行一個簡單的任務邏輯, 不得不加上 6 行冗繁的樣板代碼。 那如果是lambda該怎麼做?
Runnable r = -> System.out.println("do something.");嗯, 這代碼看起來很酷, 你可以看到我們用和->的方式完成了這件事,
在lambda中我們遵循如下的運算式來編寫:
expression = (variable) -> action variable: 這是一個變數,一個預留位置。 像x,y,z,可以是多個變數;action: 這裡我稱它為action, 這是我們實現的代碼邏輯部分,它可以是一行代碼也可以是一個代碼片段。可以看到Java中lambda運算式的格式:參數、箭頭、以及動作實現, 當一個動作實現無法用一行代碼完成, 可以編寫 一段代碼用{}包裹起來。
lambda運算式可以包含多個參數,例如:
這時候我們應該思考這段代碼不是之前的x和y數字相加, 而是創建了一個函數, 用來計算兩個運算元的和。 後面用int類型進行接收, 在lambda中為我們省略去了return。
函數式介面函數式介面是只有一個方法的介面, 用作lambda運算式的類型。 前面寫的例子就是一個函數式介面, 來看看jdk中的Runnable源碼
@FunctionalInterfacepublic interface Runnable { /** * When an object implementing interfaceRunnable
is used * to create a thread, starting the thread causes the object's * run
method to be called in that separately executing * thread. * * The general contract of the method run
is that it may * take any action whatsoever. * * @see java.lang.Thread#run */ public abstract void run;}
這裡只有一個抽象方法run, 實際上你不寫public abstract也是可以的, 在介面中定義的方法都是public abstract的。 同時也使用注解@FunctionalInterface告訴編譯器這是一個函數式介面, 當然你不這麼寫也可以, 標識後明確了這個函數中 只有一個抽象方法, 當你嘗試在介面中編寫多個方法的時候編譯器將不允許這麼幹。
嘗試函數式介面我們來編寫一個函數式介面, 輸入一個年齡, 判斷這個人是否是成人。
從這個例子我們很輕鬆的完成 是否是成人 的動作, 其次判斷是否是成人, 在此之前我們的做法一般是編寫一個 判斷是否是成人的方法, 是無法將 判斷 共用的。 而在本例只, 你要做的是將 行為 (判斷是否是成人, 或者是判斷是否大於30歲) 傳遞進去, 函數式介面告訴你結果是什麼。
實際上諸如上述例子中的介面, 偉大的jdk設計者為我們準備了java.util.function包
我們前面寫的Predicate函數式介面也是JDK種的一個實現, 他們大致分為以下幾類:
消費型介面示例public static void donation(Integer money, Consumer轉換字串為Integer
public static Integer convert(String str, Function篩選出只有2個字的水果
public static List在Java語言中, 一個介面中定義的方法必須由實現類提供實現。 但是當介面中加入新的API時, 實現類按照約定也要修改實現, 而Java8的API對現有介面也添加了很多方法, 比如List介面中添加了sort方法。 如果按照之前的做法, 那麼所有的實現類都要實現sort方法, JDK的編寫者們一定非常抓狂。
幸運的是我們使用了Java8,這一問題將得到很好的解決,在Java8種引入新的機制,支援在介面中聲明方法同時提供實現。 這令人激動不已,你有兩種方式完成 1.在介面內聲明靜態方法 2.指定一個默認方法。
我們來看看在JDK8中上述List介面添加方法的問題是如何解決的
default void sort(Comparator super E> c) { Object a = this.toArray; Arrays.sort(a, (Comparator) c); ListIterator翻閱List介面的源碼,其中加入一個默認方法default void sort(Comparator super E> c)。 在返回值之前加入default關鍵字,有了這個方法我們可以直接調用sort方法進行排序。
ListComparator.naturalOrder是一個自然排序的實現,這裡可以自訂排序方案。你經常看到使用Java8操作集合的時候可以直接foreach的原因也是在Iterable介面中也新增了一個默認方法:forEach,該方法功能和 for 迴圈類似,但是允許 用戶使用一個Lambda運算式作為循環體。
JDK的編寫者們一定非常抓狂。幸運的是我們使用了Java8,這一問題將得到很好的解決,在Java8種引入新的機制,支援在介面中聲明方法同時提供實現。 這令人激動不已,你有兩種方式完成 1.在介面內聲明靜態方法 2.指定一個默認方法。
我們來看看在JDK8中上述List介面添加方法的問題是如何解決的
default void sort(Comparator super E> c) { Object a = this.toArray; Arrays.sort(a, (Comparator) c); ListIterator翻閱List介面的源碼,其中加入一個默認方法default void sort(Comparator super E> c)。 在返回值之前加入default關鍵字,有了這個方法我們可以直接調用sort方法進行排序。
ListComparator.naturalOrder是一個自然排序的實現,這裡可以自訂排序方案。你經常看到使用Java8操作集合的時候可以直接foreach的原因也是在Iterable介面中也新增了一個默認方法:forEach,該方法功能和 for 迴圈類似,但是允許 用戶使用一個Lambda運算式作為循環體。