您的位置:首頁>正文

跟上Java8–瞭解lambda

從java8出現以來lambda是最重要的特性之一, 它可以讓我們用簡潔流暢的代碼完成一個功能。 很長一段時間java被吐槽是冗餘和缺乏函數式程式設計能力的語言, 隨著函數式程式設計的流行java8種也引入了 這種程式設計風格。 在此之前我們都在寫匿名內部類幹這些事, 但有時候這不是好的做法, 本文中將介紹和使用lambda, 帶你體驗函數式程式設計的魔力。

什麼是lambda?

lambda運算式是一段可以傳遞的代碼, 它的核心思想是將物件導向中的傳遞資料變成傳遞行為。 我們回顧一下在使用java8之前要做的事, 之前我們編寫一個執行緒時是這樣的:

Runnable r = new Runnable { @Override public void run { System.out.println("do something."); }}

也有人會寫一個類去實現Runnable介面, 這樣做沒有問題, 我們注意這個介面中只有一個run方法, 當把Runnable物件給Thread物件作為構造參數時創建一個執行緒, 運行後將輸出do something.。 我們使用匿名內部類的方式實現了該方法。

這實際上是一個代碼即資料的例子, 在run方法中是執行緒要執行的一個任務, 但上面的代碼中任務內容已經被規定死了。 當我們有多個不同的任務時, 需要重複編寫如上代碼。

設計匿名內部類的目的, 就是為了方便 Java 程式師將代碼作為資料傳遞。 不過, 匿名內部 類還是不夠簡便。 為了執行一個簡單的任務邏輯, 不得不加上 6 行冗繁的樣板代碼。 那如果是lambda該怎麼做?

Runnable r = -> System.out.println("do something.");

嗯, 這代碼看起來很酷, 你可以看到我們用和->的方式完成了這件事,

這是一個沒有名字的函數, 也沒有人和參數, 再簡單不過了。 使用->將參數和實現邏輯分離, 當運行這個執行緒的時候執行的是->之後的代碼片段, 且編譯器幫助我們做了類型推導; 這個代碼片段可以是用{}包含的一段邏輯。 下面一起來學習一下lambda的語法。

基礎語法

在lambda中我們遵循如下的運算式來編寫:

expression = (variable) -> action variable: 這是一個變數,一個預留位置。 像x,y,z,可以是多個變數;action: 這裡我稱它為action, 這是我們實現的代碼邏輯部分,它可以是一行代碼也可以是一個代碼片段。

可以看到Java中lambda運算式的格式:參數、箭頭、以及動作實現, 當一個動作實現無法用一行代碼完成, 可以編寫 一段代碼用{}包裹起來。

lambda運算式可以包含多個參數,例如:

int sum = (x, y) -> x + y;

這時候我們應該思考這段代碼不是之前的x和y數字相加, 而是創建了一個函數, 用來計算兩個運算元的和。 後面用int類型進行接收, 在lambda中為我們省略去了return。

函數式介面

函數式介面是只有一個方法的介面, 用作lambda運算式的類型。 前面寫的例子就是一個函數式介面, 來看看jdk中的Runnable源碼

@FunctionalInterfacepublic interface Runnable { /** * When an object implementing interface Runnable 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告訴編譯器這是一個函數式介面, 當然你不這麼寫也可以, 標識後明確了這個函數中 只有一個抽象方法, 當你嘗試在介面中編寫多個方法的時候編譯器將不允許這麼幹。

嘗試函數式介面

我們來編寫一個函數式介面, 輸入一個年齡, 判斷這個人是否是成人。

public class FunctionInterfaceDemo { @FunctionalInterface interface Predicate { boolean test(T t); } /** * 執行Predicate判斷 * * @param age 年齡 * @param predicate Predicate函數式介面 * @return 返回布林類型結果 */ public static boolean doPredicate(int age, Predicate predicate) { return predicate.test(age); } public static void main(String[] args) { boolean isAdult = doPredicate(20, x -> x >= 18); System.out.println(isAdult); }}

從這個例子我們很輕鬆的完成 是否是成人 的動作, 其次判斷是否是成人, 在此之前我們的做法一般是編寫一個 判斷是否是成人的方法, 是無法將 判斷 共用的。 而在本例只, 你要做的是將 行為 (判斷是否是成人, 或者是判斷是否大於30歲) 傳遞進去, 函數式介面告訴你結果是什麼。

實際上諸如上述例子中的介面, 偉大的jdk設計者為我們準備了java.util.function包

我們前面寫的Predicate函數式介面也是JDK種的一個實現, 他們大致分為以下幾類:

消費型介面示例public static void donation(Integer money, Consumer consumer){ consumer.accept(money); }public static void main(String[] args) { donation(1000, money -> System.out.println("好心的麥樂迪為Blade捐贈了"+money+"元")) ;}供給型介面示例public static List supply(Integer num, Supplier supplier){ List resultList = new ArrayList ; for(int x=0;x list = supply(10, -> (int)(Math.random*100)); list.forEach(System.out::println);}函數型介面示例

轉換字串為Integer

public static Integer convert(String str, Function function) { return function.apply(str);}public static void main(String[] args) { Integer value = convert("28", x -> Integer.parseInt(x));}斷言型介面示例

篩選出只有2個字的水果

public static List filter(List fruit, Predicate predicate){ List f = new ArrayList<>; for (String s : fruit) { if(predicate.test(s)){ f.add(s); } } return f;}public static void main(String[] args) { List fruit = Arrays.asList("香蕉", "哈密瓜", "榴槤", "火龍果", "水蜜桃"); List newFruit = filter(fruit, (f) -> f.length == 2); System.out.println(newFruit);}預設方法

在Java語言中, 一個介面中定義的方法必須由實現類提供實現。 但是當介面中加入新的API時, 實現類按照約定也要修改實現, 而Java8的API對現有介面也添加了很多方法, 比如List介面中添加了sort方法。 如果按照之前的做法, 那麼所有的實現類都要實現sort方法, JDK的編寫者們一定非常抓狂。

幸運的是我們使用了Java8,這一問題將得到很好的解決,在Java8種引入新的機制,支援在介面中聲明方法同時提供實現。 這令人激動不已,你有兩種方式完成 1.在介面內聲明靜態方法 2.指定一個默認方法。

我們來看看在JDK8中上述List介面添加方法的問題是如何解決的

default void sort(Comparator c) { Object a = this.toArray; Arrays.sort(a, (Comparator) c); ListIterator i = this.listIterator; for (Object e : a) { i.next; i.set((E) e); }}

翻閱List介面的源碼,其中加入一個默認方法default void sort(Comparator c)。 在返回值之前加入default關鍵字,有了這個方法我們可以直接調用sort方法進行排序。

List list = Arrays.asList(2, 7, 3, 1, 8, 6, 4);list.sort(Comparator.naturalOrder);System.out.println(list);

Comparator.naturalOrder是一個自然排序的實現,這裡可以自訂排序方案。你經常看到使用Java8操作集合的時候可以直接foreach的原因也是在Iterable介面中也新增了一個默認方法:forEach,該方法功能和 for 迴圈類似,但是允許 用戶使用一個Lambda運算式作為循環體。

JDK的編寫者們一定非常抓狂。

幸運的是我們使用了Java8,這一問題將得到很好的解決,在Java8種引入新的機制,支援在介面中聲明方法同時提供實現。 這令人激動不已,你有兩種方式完成 1.在介面內聲明靜態方法 2.指定一個默認方法。

我們來看看在JDK8中上述List介面添加方法的問題是如何解決的

default void sort(Comparator c) { Object a = this.toArray; Arrays.sort(a, (Comparator) c); ListIterator i = this.listIterator; for (Object e : a) { i.next; i.set((E) e); }}

翻閱List介面的源碼,其中加入一個默認方法default void sort(Comparator c)。 在返回值之前加入default關鍵字,有了這個方法我們可以直接調用sort方法進行排序。

List list = Arrays.asList(2, 7, 3, 1, 8, 6, 4);list.sort(Comparator.naturalOrder);System.out.println(list);

Comparator.naturalOrder是一個自然排序的實現,這裡可以自訂排序方案。你經常看到使用Java8操作集合的時候可以直接foreach的原因也是在Iterable介面中也新增了一個默認方法:forEach,該方法功能和 for 迴圈類似,但是允許 用戶使用一個Lambda運算式作為循環體。

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示