日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java 8 - 01 优雅编程 lambda 以及 @FunctionalInterface注解一点通

發(fā)布時(shí)間:2025/3/21 java 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 8 - 01 优雅编程 lambda 以及 @FunctionalInterface注解一点通 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • Pre
  • 為啥需要lambda
  • lambda的妙用
    • @FunctionalInterface

Pre

在軟件工程中,不管你做什么,用戶的需求肯定會變的,如何應(yīng)對這樣不斷變化的需求? 理想的狀態(tài)下,應(yīng)該把你的工作量降到最少。 此外,類似的新功能實(shí)現(xiàn)起來還應(yīng)該很簡單,而且易于長期維護(hù)。

行為參數(shù)化就是可以幫助我們處理頻繁變更的需求的一種軟件開發(fā)模式。

簡言之,它意味著拿出一個(gè)代碼塊,把它準(zhǔn)備好卻不去執(zhí)行它。這個(gè)代碼塊以后可以被我的程序的其他部分調(diào)用,這意味著我可以推遲這塊代碼的執(zhí)行。例如,我可以將代碼塊作為參數(shù)傳遞給另一個(gè)方法,稍后再去執(zhí)行它。這樣,這個(gè)方法的行為就基于那塊代碼被參數(shù)化了。


為啥需要lambda

舉個(gè)例子哈: 有個(gè)項(xiàng)目,老板說需要組建一個(gè)研發(fā)團(tuán)隊(duì),巴拉巴拉~

老板說 來倆Java程序猿

public class EnginnerTest {public List getJavaEngineers(List<Enginner> enginnerList){List javaEnginnerList = new ArrayList();for (Enginner enginner: enginnerList) {if ("Java".equals(enginner.getJob())){javaEnginnerList.add(enginner);}}return javaEnginnerList;}public static void main(String[] args) {List<Enginner> enginnerList = Arrays.asList(new Enginner("Java", 18), new Enginner("GO", 20), new Enginner("Python", 15), new Enginner("DBA", 15),new Enginner("Java", 25));EnginnerTest enginnerTest= new EnginnerTest();List javaEngineers = enginnerTest.getJavaEngineers(enginnerList);System.out.println(javaEngineers);} }

來看下重點(diǎn) "Java".equals(enginner.getJob())

隔天,老板說,再來個(gè)GO的…

我在弄個(gè) getGoEngineers方法? 那要是老板在要個(gè)Python的 , C++的 ,那我這里豈不是滿屏的Ctrl +C ,Ctrl +V ?

一個(gè)良好的原則是在編寫類似的代碼之后,嘗試將其抽象化。

一種做法是給方法加一個(gè)參數(shù),把Job變成參數(shù),這樣就能靈活地適應(yīng)變化了

public List getEngineersByJob(List<Enginner> enginnerList , String job){List targetEngineerList = new ArrayList();for (Enginner enginner: enginnerList) {if (job.equals(enginner.getJob())){targetEngineerList.add(enginner);}}return targetEngineerList;}

結(jié)果,老板又說了,我要幾個(gè)有開發(fā)經(jīng)驗(yàn)的工程師,這。。。。

那我寫這么一個(gè)方法,你看可好?

public List getEngineersByJobAndAge(List<Enginner> enginnerList , String job,int age){List targetEngineerList = new ArrayList();for (Enginner enginner: enginnerList) {if (job.equals(enginner.getJob()) && enginner.getAge() > age){targetEngineerList.add(enginner);}}return targetEngineerList;}

我只是Ctrl+C了大部分的代碼稍微修改下,實(shí)現(xiàn)了這個(gè)需求。 但卻不是一個(gè)好的辦法, 因?yàn)樗蚱屏薉RY(Don’t Repeat Yourself,不要重復(fù)自己)的軟件工程原則。 如果你想要改變篩選遍歷方式來提升性能呢?那就得修改所有方法的實(shí)現(xiàn),而不是只改一個(gè)。從工程工作量的角度來看,這代價(jià)太大了 。

那改怎么辦比較理想呢?

策略設(shè)計(jì)模式了解下

選擇Enginner的不同策略

你需要一種比添加很多參數(shù)更好的方法來應(yīng)對變化的需求。讓我們后退一步來看看更高層次的抽象。

一種可能的解決方案是對你的選擇標(biāo)準(zhǔn)建模:你考慮是 Enginner,需要根據(jù) Enginner的某些屬性(比如它是Java的嗎? 年齡超過30嗎?)來返回一個(gè)
boolean 值。

我們把它稱為謂詞(即一個(gè)返回 boolean 值的函數(shù))。讓我們定義一個(gè)接口來對選擇標(biāo)準(zhǔn)建模:

public interface EnginnerFilter {boolean getMatchedEnginner(Enginner enginner); }

現(xiàn)在你就可以用 EnginnerFilter 的多個(gè)實(shí)現(xiàn)代表不同的選擇標(biāo)準(zhǔn)了,比如

public class JavaEnginnerFilter implements EnginnerFilter {@Overridepublic boolean getMatchedEnginner(Enginner enginner) {return "Java".equals(enginner.getJob()); // 僅篩選Java的} } public class AgeGte30JavaEnginnerFilter implements EnginnerFilter {@Overridepublic boolean getMatchedEnginner(Enginner enginner) {return "Java".equals(enginner.getJob()) && enginner.getAge() >= 30; // 篩選Java 并且年齡>=30的 } }

你可以把這些標(biāo)準(zhǔn)看作 filter 方法的不同行為 , 它讓你定義一族算法,把它們封裝起來(稱為“策略”),然后在運(yùn)行時(shí)選擇一個(gè)算法 . 在這里,算法族就是 EnginnerFilter ,不同的策略就是 AgeGte30JavaEnginnerFilter和 JavaEnginnerFilter 。

但是,該怎么利用 EnginnerFilter 的不同實(shí)現(xiàn)呢?你需要 getMatchedEnginner 方法接受 Enginner對象,對 Enginner做條件測試。這就是行為參數(shù)化:讓方法接受多種行為作為參數(shù),并在內(nèi)部使用,來完成不同的行為。

要在我們的例子中實(shí)現(xiàn)這一點(diǎn),你要給 getMatchedEnginner 方法添加一個(gè)參數(shù),讓它接受Enginner對象。

這在軟件工程上有很大好處:現(xiàn)在你把 getMatchedEnginner方法迭代集合的邏輯與你要應(yīng)用到集合中每個(gè)元素的行為(這里是一個(gè)謂詞)區(qū)分開了。


利用 EnginnerFilter 改過之后,方法如下

public List findEnginner(List<Enginner> enginnerList, EnginnerFilter filter){List targetEngineerList = new ArrayList();for (Enginner enginner: enginnerList) {if (filter.getMatchedEnginner(enginner)){ // 謂詞對象封裝了測試Enginner 的條件targetEngineerList.add(enginner);}}return targetEngineerList;}

測試下

List targetEngineerList4 = enginnerTest.findEnginner(enginnerList,new AgeGte30JavaEnginnerFilter());System.out.println(targetEngineerList4);List targetEngineerList5 = enginnerTest.findEnginner(enginnerList,new JavaEnginnerFilter());System.out.println(targetEngineerList5);

這段代碼比我們第一次嘗試的時(shí)候靈活多了,讀起來、用起來也更容易!現(xiàn)在你可以創(chuàng)建不同的 EnginnerFilter對象,并將它們傳遞給 getMatchedEnginner方法。

當(dāng)然了,也可以用匿名內(nèi)部類

List targetEngineerList6 = enginnerTest.findEnginner(enginnerList, new EnginnerFilter() {@Overridepublic boolean getMatchedEnginner(Enginner enginner) {return "Python".equals(enginner.getJob());}});

在通往抽象的路上,我們還可以更進(jìn)一步。目前, getMatchedEnginner方法還只適用于 Enginner 。 你還可以將 List 類型抽象化,從而兼容更多類型

public interface Filter<T> {boolean filter(T t); } public static <T> List filter(List<T> list, Filter<T> p) {List<T> targetList = new ArrayList();for (T t : list) {if (p.filter(t)) targetList.add(t);}return targetList;}

lambda的妙用

上面的代碼是不是很長 ?

讓我們在接口上增加一個(gè)注解 @FunctionalInterface (標(biāo)注這個(gè)接口是一個(gè)function的接口) 【可選操作】

@FunctionalInterface public interface EnginnerFilter {boolean getMatchedEnginner(Enginner enginner); }

使用lambda ,

List targetEngineerList7 = enginnerTest.findEnginner(enginnerList,(Enginner enginer) -> {return "Java".equals(enginer.getJob());});System.out.println(targetEngineerList7);

(Enginner enginer) -> 這個(gè)地方還可以簡寫,因?yàn)槟愕腅nginnerFilter 這個(gè)接口的入?yún)⒕褪荅nginner 類型的,lambda自己可以反推到是這個(gè)類型,可以簡寫為 (enginer) -> ,就1個(gè)參數(shù),也就沒有用括號的必要了,最后就成了這樣子

enginnerTest.findEnginner(enginnerList,engineer -> {return "Java".equals(engineer.getJob());});

@FunctionalInterface

  • Java 8為函數(shù)式接口引入了一個(gè)新注解@FunctionalInterface,主要用于編譯級錯(cuò)誤檢查,加上該注解,當(dāng)你寫的接口不符合函數(shù)式接口定義的時(shí)候,編譯器會報(bào)錯(cuò)。

  • 加不加@FunctionalInterface對于接口是不是函數(shù)式接口沒有影響,該注解知識提醒編譯器去檢查該接口是否僅包含一個(gè)抽象方法

  • 標(biāo)注了@FunctionalInterface , 抽象方法只能包含一個(gè) , default 方法 和 static的方法除外 ,看下面 @FunctionalInterface 并沒有報(bào)錯(cuò) (可以看看Comparator接口的定義)

JDK8 中的好多接口都加了 @FunctionInterface ,比如Runnable 、 Comparator

總結(jié)

以上是生活随笔為你收集整理的Java 8 - 01 优雅编程 lambda 以及 @FunctionalInterface注解一点通的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。