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