java8中的lambda表達(dá)式,看這篇絕對(duì)夠
Lambda表達(dá)式
Lambda是簡(jiǎn)潔的標(biāo)識(shí)可傳遞匿名函數(shù)的一種方式。“互動(dòng)”事件驅(qū)動(dòng)下,最終面向?qū)ο缶幊毯秃瘮?shù)式編程結(jié)合才是趨勢(shì)。 java中,一段代碼的傳遞并不容易。因?yàn)镴AVA是面向?qū)ο蟮恼Z(yǔ)言,如果要傳遞一段代碼,必須先構(gòu)建類,再生成對(duì)應(yīng)的對(duì)象來(lái)傳遞所要的代碼。
在之前,JAVA的設(shè)計(jì)者都抗拒加入這一特性,雖然JAVA現(xiàn)有的特性也能通過(guò)類和對(duì)象實(shí)現(xiàn)類似的API但是這樣復(fù)雜且不易于使用。在后期,問(wèn)題早已不是JAVA是不是要變成一門使用函數(shù)式編程的語(yǔ)言,而是如何實(shí)現(xiàn)這種改變。
在java8之前已經(jīng)有了多年的實(shí)驗(yàn),然后JAVA8來(lái)了。
特性
- 匿名:lambda表達(dá)式不像面向?qū)ο蟮姆椒ㄒ粯?,有確定的名稱。
- 函數(shù):雖然lambda不是對(duì)象的方法,屬于某個(gè)特定的類。但是lambda表達(dá)式一樣的有參數(shù)列表、函數(shù)主體 返回類型和異常聲明
- 傳遞:lambda表達(dá)式可以作為參數(shù)傳遞
- 簡(jiǎn)潔:無(wú)需像匿名類一樣有固定模板的代碼,lambda寫得少而想得多
- JAVA8中 可以為接口增加靜態(tài)方法、可以為類增加默認(rèn)方法
一、lambda表達(dá)式介紹
1.1 lambda表達(dá)式結(jié)構(gòu)
1.2 常見(jiàn)的Lambda表達(dá)式
//1、單個(gè)參數(shù) (String s)->s.length() //2、單個(gè)對(duì)象 (Apple a)->a.getWeight()>150 //3、多參數(shù),多語(yǔ)句 (int a,int b)->{ System.out.println(a); System.out.println(b); } //4、空參數(shù),返回int值42 ()->42 //5、多對(duì)象參數(shù) (Applea1,Applea2)->a1.getWeight().compareTo(a2.getWeight())
1.3 基本語(yǔ)法
- (參數(shù)…)-> 表達(dá)式 隱式返回表達(dá)式結(jié)果
- (參數(shù)…)->{執(zhí)行語(yǔ)句} 可用return語(yǔ)句 顯示返回執(zhí)行結(jié)果
- 函數(shù)式接口不允許拋出受檢異常
- 注意:當(dāng)參數(shù)只有一個(gè)時(shí),也可以去掉參數(shù)的括號(hào)。原因是java編譯器的自動(dòng)類型推斷
1.4 類型檢查
- Lambda的類型由上下文推斷而來(lái)
- 同樣的lambda表達(dá)式,不同的函數(shù)式接口,只要方法的簽名一致,同樣的表達(dá)式可以用于不同的函數(shù)是接口。
- 只有函數(shù)式接口的實(shí)現(xiàn),能承載lambda表達(dá)式
- Objecto=()-{System.out.print("HellowWorld")}這是不合法的 因?yàn)镺bject不是一個(gè)函數(shù)式接口
1.5 類型推斷
Lambda表達(dá)式可以省略參數(shù)的類型,java編譯器能自動(dòng)推斷
當(dāng)lambda只有一個(gè)參數(shù)需要推斷類型時(shí),參數(shù)兩邊的括號(hào)可以省略
List<Apple> c=filter(inventory,a->"green".equals(a.getColor())); Comparator<Apple> c=(a1,a2)->a1.getWeight.compareTo(a2.getWeight());
1.6 變量作用域
JAVA8之前 內(nèi)部類只允許訪問(wèn)final修飾的變量,現(xiàn)在使用lambda表達(dá)式,一個(gè)內(nèi)部類可以訪問(wèn)任何有效的final局部變量-任何值不會(huì)發(fā)生變化的變量
- java限制了 lambda表達(dá)式訪問(wèn)的自由變量,值是不可更改的,因?yàn)檫@會(huì)導(dǎo)致出現(xiàn)無(wú)法預(yù)料的并發(fā)問(wèn)題。
- java編譯器的限制是有限的,只對(duì)局部變量有效,如果使用靜態(tài)變量,或者示例變量,編譯器不會(huì)提示任何錯(cuò)誤。這樣仍然是不安全的。
- 可以用數(shù)組 int[] counter =new int[1]; button.setOnAaction(event->counter[0]++); 任然可以讓lambda對(duì)局部變量進(jìn)行重新賦值。
- lambda表達(dá)式的方法體,與被嵌套的代碼塊具有同樣的作用域,所有適用同樣的命名沖突和變量屏蔽規(guī)則。
1.7 方法引用
對(duì)于已有的方法,如果希望作為lambda表達(dá)式來(lái)使用,可以直接使用方法引用
三種方法引用的情況
- 對(duì)象::實(shí)例方法
- 類::靜態(tài)方法
- 類::實(shí)例方法
在第一種和第二種方法引用種,方法的引用等于提供方法參數(shù)的lambda表達(dá)式
例如:
- System.out::println() 等同于 System.out.print(x)
- Math::pow 等同于 (x,y)->Math.pow(x,y)
對(duì)于第三種,則相當(dāng)于第一個(gè)參數(shù)成為執(zhí)行方法的對(duì)象
例如:String::compareToIngnoreCase 等同于(x,y) x.compareIngoreCase(Y);
1.8 構(gòu)造器引用
對(duì)于構(gòu)造器引用,相當(dāng)于根據(jù)構(gòu)造器的方法的參數(shù),生成一個(gè)構(gòu)造的對(duì)象的一個(gè)lambda表達(dá)式
例如:StringBuilder::new 可以表示為 (Stiring s)->new StringBuilder(s); 具體引用哪個(gè)構(gòu)造器,編譯器會(huì)根據(jù)上下文推斷使用符合參數(shù)的構(gòu)造器。
二、在何處使用lambda表達(dá)式
2.1 函數(shù)式接口介紹
總結(jié):就是只定義了一個(gè)抽象方法的接口,即使有一堆的default方法(default方法是為了增強(qiáng)某些API但避免現(xiàn)有大范圍改動(dòng)所有API所以推出了默認(rèn)方法)
不同接口的默認(rèn)方法沖突問(wèn)題
如果實(shí)現(xiàn)的接口已有一個(gè)默認(rèn)方法,但是另一個(gè)父類或者接口也有同樣的默認(rèn)方法。
- 如果是父類和接口默認(rèn)方法一致,那么直接使用父類的方法實(shí)現(xiàn),忽略接口中的默認(rèn)方法(類優(yōu)先規(guī)則,如果嘗試重寫默認(rèn)方法toString 那么永遠(yuǎn)都不會(huì)優(yōu)于Object的toString)
- 如果一個(gè)父接口提供了一個(gè)默認(rèn)方法,另一個(gè)接口也提供了同名稱和參數(shù)的方法(不論是否默認(rèn)方法)那么都必須覆蓋改方法。
其他:
接口中重寫Object類的方法,例如 Comparator 一般是為了關(guān)聯(lián)javadoc的注釋。
2.2 常見(jiàn)的函數(shù)式接口
介紹:函數(shù)式接口的抽象方法的簽名,基本就是lambda表達(dá)式的簽名,這種抽象方法稱為 函數(shù)描述符
Predicate接口
方法簽名為,輸入某個(gè)對(duì)象 返回布爾結(jié)果
/** * java.util.Predicate 是一個(gè)只有test方法,返回布爾值的一個(gè)函數(shù)式接口, * 與其類似的還有用于比較,排序的Comparator接口,其只有一個(gè)返回整數(shù)的比較接口 * @param list * @param p * @param <T> * @return */ public static <T> List<T> filter(List<T> list, Predicate<T> p){ List<T> result=new ArrayList<>(); for (T t : list) { if (p.test(t)) result.add(t); } return result; } public static void main(String[] args) { //Predicate函數(shù)式接口示例 List<Apple> appleList=new ArrayList<>(); List<Apple> resulAppleList=filter(appleList,(Apple a)->a.getColor().equals("red")); }
Counsumer接口
Accept ()方法簽名為,輸入某個(gè)對(duì)象 返回void
/** * 常用2:Consume * consume接口定義了一個(gè) 名為accept的抽象方法,接收泛型 T 返回void * 可用來(lái)訪問(wèn)T類型的對(duì)象,并且執(zhí)行某些操作。 * 如下用其創(chuàng)建,一個(gè)foreach方法,可以實(shí)現(xiàn)對(duì)所有List的遍歷。且對(duì)每個(gè)對(duì)象執(zhí)行consume定義的操作。 * 該foreach方法,java8之后成了List接口的default方法。 * @param list * @param <T> */ public static <T> void foreach(List<T> list, Consumer<T> consumer){ for (T t : list) { consumer.accept(t); } } //Consume函數(shù)式接口示例,遍歷列表執(zhí)行某項(xiàng)操作 foreach(appleList,(Apple a)->{if (a.getColor()==null);a.setColor("garly");}); appleList.forEach((Apple a)->{if (a.getColor()==null);a.setColor("garly");});
Function接口
Apply() 方法簽名:輸入某個(gè)對(duì)象、返回某個(gè)對(duì)象
/** * 常用2:Consume * consume接口定義了一個(gè) 名為accept的抽象方法,接收泛型 T 返回void * 可用來(lái)訪問(wèn)T類型的對(duì)象,并且執(zhí)行某些操作。 * 如下用其創(chuàng)建,一個(gè)foreach方法,可以實(shí)現(xiàn)對(duì)所有List的遍歷。且對(duì)每個(gè)對(duì)象執(zhí)行consume定義的操作。 * 該foreach方法,java8之后成了List接口的default方法。 * @param list * @param <T> */ public static <T> void foreach(List<T> list, Consumer<T> consumer){ for (T t : list) { consumer.accept(t); } } //Consume函數(shù)式接口示例,遍歷列表執(zhí)行某項(xiàng)操作 foreach(appleList,(Apple a)->{if (a.getColor()==null);a.setColor("garly");}); appleList.forEach((Apple a)->{if (a.getColor()==null);a.setColor("garly");});
2.3 常見(jiàn)的Lambda和已有的實(shí)現(xiàn)
案例 | Lambda例子 | 對(duì)應(yīng)的函數(shù)式接口 |
---|---|---|
布爾表達(dá)式 | (List list) ->list.isEmpty() | Predicate<List |
創(chuàng)建對(duì)象 | ()->new APPle() | Supplier |
消費(fèi)一個(gè)對(duì)象 | (Apple a->{sout(a.getColor());} | Consumer |
從一個(gè)對(duì)象中提取 | (Apple a)>a.geWeight() | Function 或者其特殊化的 ToIntFunction |
合并兩個(gè)值 | (int a,int b)->a+b | IntBinaryOperator |
比較兩個(gè)對(duì)象 | (Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight()) | Comparator BigFunction<Apple,Apple,Integer> ToIntBigFunction<Apple,Apple> |
2.4 針對(duì)裝箱拆箱的優(yōu)化
java的基本類型和引用類型之間,會(huì)自動(dòng)的進(jìn)行拆箱裝箱,但是本質(zhì)是吧原始類型包裹起來(lái)再保存在堆內(nèi)存,所以裝箱后需要更多內(nèi)存。java位基本的類型定義了特有的函數(shù)式接口,一般只需要加上原始類型的前綴即可
輸入基本類型的函數(shù)式接口:
DoublePredict
IntConsumer
LongBinaryOperate
輸出基本類型的函數(shù)式接口:
ToIntFunction
2.5 復(fù)合Lambda函數(shù)
List<Apple>apples=newArrayList<>(); apples.add(newApple("red",11)); apples.add(newApple("red",12)); apples.add(newApple("green",13)); /** *對(duì)排序lanmbda進(jìn)行復(fù)復(fù)合-比較器鏈 *1、默認(rèn)逆序方法:reversed() *2、多級(jí)比較:thenComparing() *example:對(duì)apples按照顏色排序后,進(jìn)行逆序,如果顏色一樣再按照重量遞增 */ Comparator<Apple>comparator=Comparator.comparing(Apple::getColor).reversed().thenComparing(Apple::getWeight); apples.sort(comparator); /** *謂詞復(fù)合且、或、非 *1、negate否定 *2、and且 *3、or或 *example:對(duì)不是紅色的蘋果進(jìn)行過(guò)濾,且收集重量大于100的蘋果 */ Predicate<Apple>redApplePredicate=a->a.getColor().equals("red"); Predicate<Apple>notRedApple=redApplePredicate.negate(); List<Apple>notRedAppleList=apples.stream().filter(notRedApple.and(apple->apple.getWeight()>100)).collect(Collectors.toList()); /** *函數(shù)復(fù)合 *1、andThen將前一lambda執(zhí)行結(jié)果,作為后一表達(dá)式的參數(shù) *2、compose將后一表達(dá)式的結(jié)果作為前一表達(dá)式的參數(shù) *example;complexReult=g(f(x))例如g(f(1))step1:1+1=2step2:(1+1)*2+"" */ Function<Integer,Integer>f=x->x+1; Function<Integer,String>g=x->x*2+""; Function<Integer,String>complexResult1=f.andThen(g);
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Spring Data JPA系列之投影(Projection)的用法
本篇文章主要介紹了詳解Spring Data JPA系列之投影(Projection)的用法,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07springboot2版本無(wú)法加載靜態(tài)資源問(wèn)題解決
這篇文章主要介紹了springboot2版本無(wú)法加載靜態(tài)資源問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11Java并發(fā)工具類LongAdder原理實(shí)例解析
這篇文章主要介紹了Java并發(fā)工具類LongAdder原理實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05關(guān)于Spring?Ioc和DI注解的問(wèn)題
這篇文章主要介紹了Spring?Ioc和DI注解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03一文帶你了解微服務(wù)架構(gòu)中的"發(fā)件箱模式"
微服務(wù)架構(gòu)如今非常的流行,這個(gè)架構(gòu)下可能經(jīng)常會(huì)遇到“雙寫”的場(chǎng)景。本文就和大家分享一個(gè)“發(fā)件箱模式”,?感興趣的小伙伴可以了解一下2023-01-01Tree組件實(shí)現(xiàn)支持50W數(shù)據(jù)方法剖析
這篇文章主要為大家介紹了Tree組件實(shí)現(xiàn)支持50W數(shù)據(jù)的方法剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Java中高效的判斷數(shù)組中某個(gè)元素是否存在詳解
相信大家在操作Java的時(shí)候,經(jīng)常會(huì)要檢查一個(gè)數(shù)組(無(wú)序)是否包含一個(gè)特定的值?這是一個(gè)在Java中經(jīng)常用到的并且非常有用的操作。同時(shí),這個(gè)問(wèn)題在Stack Overflow中也是一個(gè)非常熱門的問(wèn)題。本文將分析幾種常見(jiàn)用法及其時(shí)間成本,有需要的朋友們可以參考借鑒。2016-11-11Java使用正則表達(dá)式實(shí)現(xiàn)找出數(shù)字功能示例
這篇文章主要介紹了Java使用正則表達(dá)式實(shí)現(xiàn)找出數(shù)字功能,結(jié)合實(shí)例形式分析了Java針對(duì)數(shù)字的匹配查找及非數(shù)字替換操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-03-03mybatis 插件: 打印 sql 及其執(zhí)行時(shí)間實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇mybatis 插件: 打印 sql 及其執(zhí)行時(shí)間實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06