Java8深入學(xué)習(xí)系列(一)lambda表達式介紹
前言
最近在學(xué)習(xí)java8,所以接下來會給大家介紹一系列的Java8學(xué)習(xí)內(nèi)容,那么讓我們先從lambda表達式開始。
眾所周知從java8出現(xiàn)以來lambda是最重要的特性之一,它可以讓我們用簡潔流暢的代碼完成一個功能。 很長一段時間java被吐槽是冗余和缺乏函數(shù)式編程能力的語言,隨著函數(shù)式編程的流行java8種也引入了 這種編程風(fēng)格。在此之前我們都在寫匿名內(nèi)部類干這些事,但有時候這不是好的做法,本文中將介紹和使用lambda, 帶你體驗函數(shù)式編程的魔力。
什么是lambda?
lambda表達式是一段可以傳遞的代碼,它的核心思想是將面向?qū)ο笾械膫鬟f數(shù)據(jù)變成傳遞行為。 我們回顧一下在使用java8之前要做的事,之前我們編寫一個線程時是這樣的:
Runnable r = new Runnable() { @Override public void run() { System.out.println("do something."); } }
也有人會寫一個類去實現(xiàn)Runnable接口,這樣做沒有問題,我們注意這個接口中只有一個run方法, 當(dāng)把Runnable對象給Thread對象作為構(gòu)造參數(shù)時創(chuàng)建一個線程,運行后將輸出do something.
。 我們使用匿名內(nèi)部類的方式實現(xiàn)了該方法。
這實際上是一個代碼即數(shù)據(jù)的例子,在run方法中是線程要執(zhí)行的一個任務(wù),但上面的代碼中任務(wù)內(nèi)容已經(jīng)被規(guī)定死了。 當(dāng)我們有多個不同的任務(wù)時,需要重復(fù)編寫如上代碼。
設(shè)計匿名內(nèi)部類的目的,就是為了方便 Java 程序員將代碼作為數(shù)據(jù)傳遞。不過,匿名內(nèi)部 類還是不夠簡便。 為了執(zhí)行一個簡單的任務(wù)邏輯,不得不加上 6 行冗繁的樣板代碼。那如果是lambda該怎么做?
Runnable r = () -> System.out.println("do something.");
嗯,這代碼看起來很酷,你可以看到我們用()和->的方式完成了這件事,這是一個沒有名字的函數(shù),也沒有人和參數(shù),再簡單不過了。 使用->將參數(shù)和實現(xiàn)邏輯分離,當(dāng)運行這個線程的時候執(zhí)行的是->之后的代碼片段,且編譯器幫助我們做了類型推導(dǎo); 這個代碼片段可以是用{}包含的一段邏輯。下面一起來學(xué)習(xí)一下lambda的語法。
基礎(chǔ)語法
在lambda中我們遵循如下的表達式來編寫:
expression = (variable) -> action
- variable: 這是一個變量,一個占位符。像x,y,z,可以是多個變量。
- action: 這里我稱它為action, 這是我們實現(xiàn)的代碼邏輯部分,它可以是一行代碼也可以是一個代碼片段
可以看到Java中l(wèi)ambda表達式的格式:參數(shù)、箭頭、以及動作實現(xiàn),當(dāng)一個動作實現(xiàn)無法用一行代碼完成,可以編寫 一段代碼用{}包裹起來。
lambda表達式可以包含多個參數(shù),例如:
int sum = (x, y) -> x + y;
這時候我們應(yīng)該思考這段代碼不是之前的x和y數(shù)字相加,而是創(chuàng)建了一個函數(shù),用來計算兩個操作數(shù)的和。 后面用int類型進行接收,在lambda中為我們省略去了return。
函數(shù)式接口
函數(shù)式接口是只有一個方法的接口,用作lambda表達式的類型。前面寫的例子就是一個函數(shù)式接口,來看看jdk中的Runnable源碼
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
這里只有一個抽象方法run,實際上你不寫public abstract
也是可以的,在接口中定義的方法都是public abstract的。 同時也使用注解@FunctionalInterface
告訴編譯器這是一個函數(shù)式接口,當(dāng)然你不這么寫也可以,標(biāo)識后明確了這個函數(shù)中 只有一個抽象方法,當(dāng)你嘗試在接口中編寫多個方法的時候編譯器將不允許這么干。
嘗試函數(shù)式接口
我們來編寫一個函數(shù)式接口,輸入一個年齡,判斷這個人是否是成人。
public class FunctionInterfaceDemo { @FunctionalInterface interface Predicate<T> { boolean test(T t); } /** * 執(zhí)行Predicate判斷 * * @param age 年齡 * @param predicate Predicate函數(shù)式接口 * @return 返回布爾類型結(jié)果 */ public static boolean doPredicate(int age, Predicate<Integer> predicate) { return predicate.test(age); } public static void main(String[] args) { boolean isAdult = doPredicate(20, x -> x >= 18); System.out.println(isAdult); } }
從這個例子我們很輕松的完成 是否是成人 的動作,其次判斷是否是成人,在此之前我們的做法一般是編寫一個 判斷是否是成人的方法,是無法將 判斷 共用的。而在本例只,你要做的是將 行為 (判斷是否是成人,或者是判斷是否大于30歲) 傳遞進去,函數(shù)式接口告訴你結(jié)果是什么。
實際上諸如上述例子中的接口,偉大的jdk設(shè)計者為我們準(zhǔn)備了java.util.function包
我們前面寫的Predicate函數(shù)式接口也是JDK種的一個實現(xiàn),他們大致分為以下幾類:
接口 | 參數(shù) | 返回值 | 類別 | 示例 |
---|---|---|---|---|
Consumer |
T | void | 消費型接口 | 輸出一個值 |
Supplier |
None | T | 供給型接口 | 工廠方法 |
Function | T | R | 函數(shù)型接口 | 獲得 Artist 對象的名字 |
Predicate |
T | boolean | 斷言型接口 | 這張唱片已經(jīng)發(fā)行了嗎 |
消費型接口示例
public static void donation(Integer money, Consumer<Integer> consumer){ consumer.accept(money); } public static void main(String[] args) { donation(1000, money -> System.out.println("好心的麥樂迪為Blade捐贈了"+money+"元")) ; }
供給型接口示例
public static List<Integer> supply(Integer num, Supplier<Integer> supplier){ List<Integer> resultList = new ArrayList<Integer>() ; for(int x=0;x<num;x++) resultList.add(supplier.get()); return resultList ; } public static void main(String[] args) { List<Integer> list = supply(10,() -> (int)(Math.random()*100)); list.forEach(System.out::println); }
函數(shù)型接口示例
轉(zhuǎn)換字符串為Integer
public static Integer convert(String str, Function<String, Integer> function) { return function.apply(str); } public static void main(String[] args) { Integer value = convert("28", x -> Integer.parseInt(x)); }
斷言型接口示例
篩選出只有2個字的水果
public static List<String> filter(List<String> fruit, Predicate<String> predicate){ List<String> f = new ArrayList<>(); for (String s : fruit) { if(predicate.test(s)){ f.add(s); } } return f; } public static void main(String[] args) { List<String> fruit = Arrays.asList("香蕉", "哈密瓜", "榴蓮", "火龍果", "水蜜桃"); List<String> newFruit = filter(fruit, (f) -> f.length() == 2); System.out.println(newFruit); }
默認(rèn)方法
在Java語言中,一個接口中定義的方法必須由實現(xiàn)類提供實現(xiàn)。但是當(dāng)接口中加入新的API時, 實現(xiàn)類按照約定也要修改實現(xiàn),而Java8的API對現(xiàn)有接口也添加了很多方法,比如List接口中添加了sort方法。 如果按照之前的做法,那么所有的實現(xiàn)類都要實現(xiàn)sort方法,JDK的編寫者們一定非常抓狂。
幸運的是我們使用了Java8,這一問題將得到很好的解決,在Java8種引入新的機制,支持在接口中聲明方法同時提供實現(xiàn)。 這令人激動不已,你有兩種方式完成 1.在接口內(nèi)聲明靜態(tài)方法 2.指定一個默認(rèn)方法。
我們來看看在JDK8中上述List接口添加方法的問題是如何解決的
default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } }
翻閱List接口的源碼,其中加入一個默認(rèn)方法default void sort(Comparator<? super E> c)
。 在返回值之前加入default關(guān)鍵字,有了這個方法我們可以直接調(diào)用sort方法進行排序。
List<Integer> list = Arrays.asList(2, 7, 3, 1, 8, 6, 4); list.sort(Comparator.naturalOrder()); System.out.println(list);
Comparator.naturalOrder()
是一個自然排序的實現(xiàn),這里可以自定義排序方案。 你經(jīng)常看到使用Java8操作集合的時候可以直接foreach的原因也是在Iterable接口中也新增了一個默認(rèn)方法:forEach , 該方法功能和 for 循環(huán)類似,但是允許 用戶使用一個Lambda表達式作為循環(huán)體。
在后面的章節(jié)中我們再次通過案例來展示函數(shù)式編程的魅力 :)
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
解決安裝mysqlclient的時候出現(xiàn)Microsoft Visual C++ 14.0 is required報錯
這篇文章主要介紹了解決安裝mysqlclient的時候出現(xiàn)Microsoft Visual C++ 14.0 is required報錯問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2018-11-11Apache Commons Math3學(xué)習(xí)之?dāng)?shù)值積分實例代碼
這篇文章主要介紹了Apache Commons Math3學(xué)習(xí)之?dāng)?shù)值積分實例代碼,涉及使用辛普森積分的例子,這里分享給大家,供需要的朋友參考。2017-10-10BufferedReader中read()方法和readLine()方法的使用
這篇文章主要介紹了BufferedReader中read()方法和readLine()方法的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04