Java 8 - 01 优雅编程 lambda 以及 @FunctionalInterface注解一点通
文章目錄
- 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)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java-COW在Java中的应用
- 下一篇: java美元兑换,(Java实现) 美元