Java 8 - 01 优雅编程 lambda 以及 @FunctionalInterface注解一点通
文章目錄
- Pre
- 為啥需要lambda
- lambda的妙用
- @FunctionalInterface
Pre
在軟件工程中,不管你做什么,用戶的需求肯定會(huì)變的,如何應(yīng)對(duì)這樣不斷變化的需求? 理想的狀態(tài)下,應(yīng)該把你的工作量降到最少。 此外,類似的新功能實(shí)現(xiàn)起來(lái)還應(yīng)該很簡(jiǎn)單,而且易于長(zhǎng)期維護(hù)。
行為參數(shù)化就是可以幫助我們處理頻繁變更的需求的一種軟件開(kāi)發(fā)模式。
簡(jiǎn)言之,它意味著拿出一個(gè)代碼塊,把它準(zhǔn)備好卻不去執(zhí)行它。這個(gè)代碼塊以后可以被我的程序的其他部分調(diào)用,這意味著我可以推遲這塊代碼的執(zhí)行。例如,我可以將代碼塊作為參數(shù)傳遞給另一個(gè)方法,稍后再去執(zhí)行它。這樣,這個(gè)方法的行為就基于那塊代碼被參數(shù)化了。
為啥需要lambda
舉個(gè)例子哈: 有個(gè)項(xiàng)目,老板說(shuō)需要組建一個(gè)研發(fā)團(tuán)隊(duì),巴拉巴拉~
老板說(shuō) 來(lái)倆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);} }來(lái)看下重點(diǎn) "Java".equals(enginner.getJob())
隔天,老板說(shuō),再來(lái)個(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é)果,老板又說(shuō)了,我要幾個(gè)有開(kāi)發(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ù)自己)的軟件工程原則。 如果你想要改變篩選遍歷方式來(lái)提升性能呢?那就得修改所有方法的實(shí)現(xiàn),而不是只改一個(gè)。從工程工作量的角度來(lái)看,這代價(jià)太大了 。
那改怎么辦比較理想呢?
策略設(shè)計(jì)模式了解下
選擇Enginner的不同策略
你需要一種比添加很多參數(shù)更好的方法來(lái)應(yīng)對(duì)變化的需求。讓我們后退一步來(lái)看看更高層次的抽象。
一種可能的解決方案是對(duì)你的選擇標(biāo)準(zhǔn)建模:你考慮是 Enginner,需要根據(jù) Enginner的某些屬性(比如它是Java的嗎? 年齡超過(guò)30嗎?)來(lái)返回一個(gè)
boolean 值。
我們把它稱為謂詞(即一個(gè)返回 boolean 值的函數(shù))。讓我們定義一個(gè)接口來(lái)對(duì)選擇標(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 方法的不同行為 , 它讓你定義一族算法,把它們封裝起來(lái)(稱為“策略”),然后在運(yùn)行時(shí)選擇一個(gè)算法 . 在這里,算法族就是 EnginnerFilter ,不同的策略就是 AgeGte30JavaEnginnerFilter和 JavaEnginnerFilter 。
但是,該怎么利用 EnginnerFilter 的不同實(shí)現(xiàn)呢?你需要 getMatchedEnginner 方法接受 Enginner對(duì)象,對(duì) Enginner做條件測(cè)試。這就是行為參數(shù)化:讓方法接受多種行為作為參數(shù),并在內(nèi)部使用,來(lái)完成不同的行為。
要在我們的例子中實(shí)現(xiàn)這一點(diǎn),你要給 getMatchedEnginner 方法添加一個(gè)參數(shù),讓它接受Enginner對(duì)象。
這在軟件工程上有很大好處:現(xiàn)在你把 getMatchedEnginner方法迭代集合的邏輯與你要應(yīng)用到集合中每個(gè)元素的行為(這里是一個(gè)謂詞)區(qū)分開(kāi)了。
利用 EnginnerFilter 改過(guò)之后,方法如下
public List findEnginner(List<Enginner> enginnerList, EnginnerFilter filter){List targetEngineerList = new ArrayList();for (Enginner enginner: enginnerList) {if (filter.getMatchedEnginner(enginner)){ // 謂詞對(duì)象封裝了測(cè)試Enginner 的條件targetEngineerList.add(enginner);}}return targetEngineerList;}測(cè)試下
List targetEngineerList4 = enginnerTest.findEnginner(enginnerList,new AgeGte30JavaEnginnerFilter());System.out.println(targetEngineerList4);List targetEngineerList5 = enginnerTest.findEnginner(enginnerList,new JavaEnginnerFilter());System.out.println(targetEngineerList5);這段代碼比我們第一次嘗試的時(shí)候靈活多了,讀起來(lái)、用起來(lái)也更容易!現(xiàn)在你可以創(chuàng)建不同的 EnginnerFilter對(duì)象,并將它們傳遞給 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的妙用
上面的代碼是不是很長(zhǎng) ?
讓我們?cè)诮涌谏显黾右粋€(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è)地方還可以簡(jiǎn)寫,因?yàn)槟愕腅nginnerFilter 這個(gè)接口的入?yún)⒕褪荅nginner 類型的,lambda自己可以反推到是這個(gè)類型,可以簡(jiǎn)寫為 (enginer) -> ,就1個(gè)參數(shù),也就沒(méi)有用括號(hào)的必要了,最后就成了這樣子
enginnerTest.findEnginner(enginnerList,engineer -> {return "Java".equals(engineer.getJob());});@FunctionalInterface
- Java 8為函數(shù)式接口引入了一個(gè)新注解@FunctionalInterface,主要用于編譯級(jí)錯(cuò)誤檢查,加上該注解,當(dāng)你寫的接口不符合函數(shù)式接口定義的時(shí)候,編譯器會(huì)報(bào)錯(cuò)。
-
加不加@FunctionalInterface對(duì)于接口是不是函數(shù)式接口沒(méi)有影響,該注解知識(shí)提醒編譯器去檢查該接口是否僅包含一個(gè)抽象方法。
-
標(biāo)注了@FunctionalInterface , 抽象方法只能包含一個(gè) , default 方法 和 static的方法除外 ,看下面 @FunctionalInterface 并沒(méi)有報(bào)錯(cuò) (可以看看Comparator接口的定義)
JDK8 中的好多接口都加了 @FunctionInterface ,比如Runnable 、 Comparator
總結(jié)
以上是生活随笔為你收集整理的Java 8 - 01 优雅编程 lambda 以及 @FunctionalInterface注解一点通的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java-COW在Java中的应用
- 下一篇: Java 8 - 02 Lambda E