摘要:但,如果加入了函數(shù)式編程,也就是將方法作為形參傳遞,這必然讓開發(fā)者為難。但是,其他語(yǔ)言早就使用了函數(shù)式編程,比如最常見(jiàn)腳本語(yǔ)言。這就是函數(shù)式編程,傳遞的是一個(gè)函數(shù)直到,才引用了函數(shù)式編程,也就是我們所說(shuō)的表達(dá)式。
導(dǎo)讀
Java從jdk1發(fā)展到今天,方法的形參類型可以是基本變量,可以是jdk自帶的類型,也可以是用戶自定義的類型,但是,方法能不能作為形參來(lái)傳遞?我們希望java能夠像其他編程語(yǔ)言,可以采用函數(shù)式編程思想,換句話說(shuō),是將方法當(dāng)做對(duì)象來(lái)看,但其在jdk1.8之前都沒(méi)有這樣做。
Java為什么在jdk1.8之前沒(méi)有這樣做?正如我們所知道的,Java自面世以來(lái),便以對(duì)象的方式立足。什么都是對(duì)象, 對(duì)象以方法傳遞消息,以屬性存儲(chǔ)數(shù)據(jù)。其又封裝了太多的底層操作,比如說(shuō)像C++的指針。因而,學(xué)習(xí)Java的人,感覺(jué)到Java簡(jiǎn)單,這也和Java的初衷相似。
但,如果加入了函數(shù)式編程,也就是將方法作為形參傳遞,這必然讓Java開發(fā)者為難。但是,其他語(yǔ)言早就使用了函數(shù)式編程,比如最常見(jiàn)JavaScript腳本語(yǔ)言。其就可以使用函數(shù)式編程,如下代碼所示:
var intArr = [1, 2, 3, 4, 5]; //這就是函數(shù)式編程,傳遞的是一個(gè)函數(shù) var newArr = intArr.map(function (item) { return item * item; }) alert(newArr);
又因?yàn)?b>return item * item;只是一條語(yǔ)句,就像是if當(dāng)中的單語(yǔ)句可以省略大括號(hào),這里也可以省略大括號(hào),如下代碼;
var newArr = intArr.map(item=> item * item)
但是,如果函數(shù)包含多條語(yǔ)句,我們不能這樣省略,還是上面的代碼。但是,條件變了,如果是偶數(shù)的話,同時(shí),結(jié)果大于10,我們才輸出來(lái)。
var intArr = [1, 2, 3, 4, 5]; //這就是函數(shù)式編程,傳遞的是一個(gè)函數(shù) var newArr = intArr.map(function (item) { var res=0; if (item % 2 == 0) { res = item * item; if (res >= 10) { return res; } } }) alert(newArr);
直到j(luò)dk8,Java才引用了函數(shù)式編程,也就是我們所說(shuō)的lambda表達(dá)式。其所對(duì)應(yīng)希臘字母的λ,讀音為拉姆達(dá)。λ在數(shù)學(xué)中即表示參數(shù),比如λ矩陣表達(dá)式。它也可以是一個(gè)表達(dá)式,也可以表示一個(gè)函數(shù)。因而,用它來(lái)命名是最為合適的。因?yàn)?,函?shù)式編程可以講方法作為形參使用。
Comparator接口在講解lambda表達(dá)式前,我們先說(shuō)說(shuō)Comparator接口,這個(gè)想必學(xué)Java的都不陌生。它是jdk1.2之后引用的,不過(guò),在jdk1.8的時(shí)候,上面加了個(gè)注解,如代碼所示:
@FunctionalInterface public interface Comparator{ 。。。 }
這個(gè)注解是什么意思?我們拆開來(lái)看,F(xiàn)unctional Interface函數(shù)式接口,只包含一個(gè)方法的接口。函數(shù)式接口有什么用?我們?cè)诤笪脑僬f(shuō)。
我們經(jīng)常以方法內(nèi)部類,來(lái)使用該接口,做容器對(duì)象的排序所用。
比如,我們現(xiàn)在有一個(gè)項(xiàng)目人員類,如代碼所示:
/** * Created By zby on 22:39 2019/3/20 */ @AllArgsConstructor @Data @NoArgsConstructor @EqualsAndHashCode @ToString public class ProjectPerson { /** * 年齡 */ private int age; /** * 姓名 */ private String name; /** * 排序 */ private int sort; }
我們使用hibernate框架獲取人員對(duì)象的集合后,像根據(jù)人員的年齡進(jìn)行排序,如代碼所示:
public static void main(String[] args) { ProjectPerson person1 = new ProjectPerson(23, "zhubaoya1", 0); ProjectPerson person2 = new ProjectPerson(33, "zhubaoya2", 1); ProjectPerson person3 = new ProjectPerson(18, "zhubaoya3", 2); ProjectPerson person4 = new ProjectPerson(17, "zhubaoya4", 3); ProjectPerson person5 = new ProjectPerson(43, "zhubaoya5", 4); ProjectPerson person6 = new ProjectPerson(35, "zhubaoya6", 5); ProjectPerson[] people = new ProjectPerson[6]; people[0] = person1; people[1] = person2; people[2] = person3; people[3] = person4; people[4] = person5; people[5] = person6; Arrays.sort(people, new Comparator() { @Override public int compare(ProjectPerson o1, ProjectPerson o2) { return o2.getAge() - o1.getAge(); } }); for (ProjectPerson person : people) { System.out.println(person); } }
按倒序輸出結(jié)果為:
ProjectPerson{age=43, name="zhubaoya5", sort=4}
ProjectPerson{age=35, name="zhubaoya6", sort=5}
ProjectPerson{age=33, name="zhubaoya2", sort=1}
ProjectPerson{age=23, name="zhubaoya1", sort=0}
ProjectPerson{age=18, name="zhubaoya3", sort=2}
ProjectPerson{age=17, name="zhubaoya4", sort=3}
我們上文也說(shuō)了,既然其是函數(shù)式編程,我們能不能將其像JavaScript那樣輸出數(shù)據(jù)呢?當(dāng)然是可以的,因?yàn)椋覀冊(cè)诰帉懙臅r(shí)候,idea就有提示,可以用lambda表達(dá)式替代,如圖所示:
于是,我們用lambda表達(dá)式替代,如代碼所示:
public static void main(String[] args) { 。。。。。 Arrays.sort(people,Comparator.comparing(ProjectPerson::getAge,(age1,age2)->Integer.compare(age2,age1))); for (ProjectPerson person : people) { System.out.println(person); } }
對(duì)于上面的表達(dá)式,我們還可以這樣寫。但不推薦這樣寫,因?yàn)?b> Integer.compare(age2,age1);是單語(yǔ)句,可以省略大括號(hào),就像上面那樣書寫。
Arrays.sort(people,Comparator.comparing(ProjectPerson::getAge,(age1,age2)->{ return Integer.compare(age2,age1); }));
其輸出的結(jié)果和上面的一樣,不過(guò),這里使用了lambda表達(dá)式,那么,什么是lambda表達(dá)式呢?
lambda表達(dá)式通過(guò)上面的表達(dá)式可以看出,lambda表達(dá)式傳遞的是函數(shù)體。我們來(lái)看Comparator.comparing這個(gè)方法,其內(nèi)部什么樣的呢?
public staticComparator comparing( Function super T, ? extends U> keyExtractor, Comparator super U> keyComparator) { Objects.requireNonNull(keyExtractor); Objects.requireNonNull(keyComparator); return (Comparator & Serializable) (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1), keyExtractor.apply(c2)); }
其有兩個(gè)參數(shù),一個(gè)是函數(shù)對(duì)象,一個(gè)比較器對(duì)象。而比較器對(duì)象是有 @FunctionalInterface注解,這是jdk1.8才有的。也就是說(shuō),不是所有的方法,都可以改成lambda表達(dá)式。
如果方法的形參類有@FunctionalInterface接口,我們一般可以向其傳遞代碼塊。這是個(gè)很神奇的地方,方法中也可以寫入方法。正如,我們可以向其傳遞代碼塊,正如Comparator.comparing(ProjectPerson::getAge,(age1,age2)->Integer.compare(age2,age1))也就是方法。
lambda表達(dá)式一般這樣表示 形參 + 箭頭-> +方法體 這三部分,即
一個(gè)代碼塊
參數(shù)
自由變量的值。這是非參數(shù)而且不在代碼中定義的變量。
單個(gè)對(duì)象的Lambda既然清楚了lambda表達(dá)式是怎么表示的,那么,我接下來(lái)就舉公司的例子:如果某個(gè)對(duì)象的屬性值不為空,我們就對(duì)該對(duì)象的屬性值操作,如以下代碼所示:
項(xiàng)目的實(shí)體類
/** * Created By zby on 21:21 2019/3/21 */ @AllArgsConstructor @NoArgsConstructor @Data public class Project { /** * 項(xiàng)目名 */ private String name; /** * 項(xiàng)目開始時(shí)間 */ private Date startTime; /** * 項(xiàng)目結(jié)束時(shí)間 */ private Date endTime; }
lambdaUtil類
/** * Created By zby on 21:22 2019/3/21 * * @param value 參數(shù)值 * @param function 可以接受任何一個(gè)參數(shù),并返回響應(yīng)的值。 */ public staticvoid ifNotNullThen(T value, Consumer function) { if (value != null) { function.accept(value); } }
Consumer也有@FunctionalInterface,其也有l(wèi)ambda表達(dá)式,如下代碼:
public static void main(String[] args) { try { // 使用阿里巴巴的fastjson框架 JSONObject jsonObject = new JSONObject(); jsonObject.put("validateTime", ""); // 使用org.apache.commons的框架 final FastDateFormat ISO_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd"); String startTime = "2018-12-22"; Project project = new Project("新華城", ISO_DATE_FORMAT.parse(startTime), new Date()); jsonObject.put("name", project.getName()); // 使用lambda表達(dá)式,輸出項(xiàng)目名稱和項(xiàng)目有效期 LambdaUtil.ifNotNullThen(project.getEndTime(), t -> jsonObject.replace("validateTime", startTime + " - " + ISO_DATE_FORMAT.format(t))); System.out.println(jsonObject); } catch (ParseException e) { e.printStackTrace(); } }
輸出結(jié)果:
{"validateTime":"2018-12-22 - 2019-03-21","name":"新華城"}
其實(shí),也可以不用lambda表達(dá)式,這樣寫也是可以的:
Date endTime=project.getEndTime(); if (endTime != null){ jsonObject.replace("validateTime", startTime + " - " + ISO_DATE_FORMAT.format(endTime)); }
為什么用了lambda表達(dá)式呢,因?yàn)?,lambda表達(dá)式可以簡(jiǎn)化代碼,讓代碼變得不那么冗余。尤其是在遍歷集合時(shí),更能顯示它的優(yōu)點(diǎn)。
集合的lambdalambdaUtil的方法
/** * Created By zby on 22:21 2019/3/21 * 過(guò)濾集合 * * @param collection 使用容器的接口,保證了松散耦合 * @param function lambda表達(dá)式的代碼塊,map一個(gè)代碼塊,以集合的方式返回 */ public static ListsimpleMap(Collection collection, Function function) { if (collection != null && collection.size() > 0) { return collection.stream().map(function).collect(Collectors.toList()); } return null; }
使用上面的過(guò)濾方法
public static void main(String[] args) { try { final FastDateFormat ISO_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd"); String startTime1 = "2018-12-22", endTime1 = "2019-03-12"; String startTime2 = "2018-12-22", endTime2 = "2019-03-14"; String startTime3 = "2018-12-22", endTime3 = "2019-03-16"; Project project1 = new Project("新華城1", ISO_DATE_FORMAT.parse(startTime1), ISO_DATE_FORMAT.parse(endTime1)); Project project2 = new Project("新華城2", ISO_DATE_FORMAT.parse(startTime2), ISO_DATE_FORMAT.parse(endTime2)); Project project3 = new Project("新華城3", ISO_DATE_FORMAT.parse(startTime3), ISO_DATE_FORMAT.parse(endTime3)); Listprojects = new ArrayList<>(); projects.add(project1); projects.add(project2); projects.add(project3); // 因?yàn)闇y(cè)試所用,上面的一般都是從數(shù)據(jù)庫(kù)讀出來(lái),封裝好的數(shù)據(jù),真正的項(xiàng)目中不會(huì)這樣寫。 List jsonObjects = LambdaUtil.simpleMap(projects, t -> { JSONObject jsonObject = new JSONObject(); jsonObject.put("name", t.getName()); LambdaUtil.ifNotNullThen(t.getStartTime(), x -> jsonObject.put("startTime", ISO_DATE_FORMAT.format(x))); LambdaUtil.ifNotNullThen(t.getEndTime(), x -> jsonObject.put("endTime",ISO_DATE_FORMAT.format(x))); jsonObject.put("validateTime", jsonObject.getString("startTime") + " - " + jsonObject.getString("endTime")); jsonObject.remove("startTime"); jsonObject.remove("endTime"); return jsonObject; }); System.out.println(jsonObjects); } catch (ParseException e) { e.printStackTrace(); }
如果,我們不使用lambda表達(dá)式,來(lái)書寫上面的代碼,會(huì)變得非常大的臃腫。然而,真正的項(xiàng)目不會(huì)這樣寫,數(shù)據(jù)一般都從數(shù)據(jù)庫(kù)讀出來(lái),然后,再用過(guò)濾的方法進(jìn)行過(guò)濾,如下面的代碼:
/** * Created By zby on 20:16 2019/1/2 * 展示列表 */ @RequestMapping(value = "/list/{id}", method = RequestMethod.GET) public Result listProjectProcess(@PathVariable Long id) { String[] PRO_JSON = {"processId", "dictCode", "dictValue", "opTime", "isChecked"}; ListprojectProcessDataList = projectProcessService.listProjectProcess(id).getResultData(); List process = simpleMap(projectProcessDataList.subList(0, projectProcessDataList.size() - 3), x -> { JSONObject json = propsFilter(x, PRO_JSON); ifNotNullThen(x.getOpTime(), t -> json.replace("opTime", t.substring(0, t.indexOf(".")))); return json; }); Project project = projectService.get(id).getResultData(); JSONObject body = new JSONObject(); body.put("projectProcess", process); body.put("resultSummary", project.getResultSummary()); return ResultUtil.buildSuccess(body); }
這才是真正的項(xiàng)目中使用的lambdaUtil,一般,我們會(huì)有架構(gòu)師將操作lambda表達(dá)式方法封裝成LambdaUtil類,我們只要在里面填充數(shù)據(jù)即可。
當(dāng)然,既然是集合,自然就有集合的排序,雖然,我們會(huì)在數(shù)據(jù)庫(kù)中排好序,再通過(guò)框架的list方法,返回出去,但是,對(duì)于枚舉的排序集合,還是能夠用的到的。
排序的lambda此外,還回頭集合排序的LambdaUtil方法,這個(gè)一般很少用,知道就可以了。
public static List simpleSort(Collection collection, Comparator comparator) { if (isNull(collection)) return null; return collection.stream().sorted(comparator).collect(toList()); }總結(jié)
知己知彼,百戰(zhàn)不殆。往架構(gòu)師的方向又邁進(jìn)了一步?。?!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73839.html
摘要:但,如果加入了函數(shù)式編程,也就是將方法作為形參傳遞,這必然讓開發(fā)者為難。但是,其他語(yǔ)言早就使用了函數(shù)式編程,比如最常見(jiàn)腳本語(yǔ)言。這就是函數(shù)式編程,傳遞的是一個(gè)函數(shù)直到,才引用了函數(shù)式編程,也就是我們所說(shuō)的表達(dá)式。 導(dǎo)讀 Java從jdk1發(fā)展到今天,方法的形參類型可以是基本變量,可以是jdk自帶的類型,也可以是用戶自定義的類型,但是,方法能不能作為形參來(lái)傳遞?我們希望java能夠像其他...
摘要:上下文比如,接受它傳遞的方法的參數(shù),或者接受它的值得局部變量中表達(dá)式需要類型稱為目標(biāo)類型。但局部變量必須顯示的聲明,或?qū)嶋H上就算。換句話說(shuō),表達(dá)式只能捕獲指派給它們的局部變量一次。注捕獲實(shí)例變量可以被看作捕獲最終局部變量。 由于第三章的內(nèi)容比較多,而且為了讓大家更好的了解Lambda表達(dá)式的使用,也寫了一些相關(guān)的實(shí)例,可以在Github或者碼云上拉取讀書筆記的代碼進(jìn)行參考。 類型檢查、...
摘要:由此可以看出,使用可以讓你的代碼在某些情況下達(dá)到何等的簡(jiǎn)潔。如果沒(méi)有參數(shù),那么前面的是必須存在的。我們知道中的,而其實(shí)就是一個(gè)只定義了一個(gè)抽象方法的。也就是說(shuō),可以訪問(wèn)定義它的那個(gè)方法的局部變量。而在里面,還可以訪問(wèn)所謂的局部變量。 上次在盆友圈發(fā)了一張照片 showImg(http://chriszou.com/images/lambda_example.png); 上面的兩段代碼是...
摘要:方法引用在之前只能進(jìn)行值傳遞,方法是不能傳遞的。首先方法接受了一個(gè)類型的對(duì)象,方法是獲取所有的文件,是用來(lái)存儲(chǔ)篩選之后的元素,循環(huán)所有獲得到的文件數(shù)組,然后調(diào)用中的方法來(lái)進(jìn)行條件篩選,放入后返回。 方法引用: 在Java 8之前只能進(jìn)行值傳遞,方法是不能傳遞的。如果你想調(diào)用一個(gè)方法你必須先獲取到它所在的類的實(shí)例,然后再通過(guò)實(shí)例去調(diào)用這個(gè)方法,但是Java 8新增了方法引用這個(gè)新特性可以...
閱讀 2938·2021-10-14 09:43
閱讀 2882·2021-10-14 09:42
閱讀 4663·2021-09-22 15:56
閱讀 2371·2019-08-30 10:49
閱讀 1594·2019-08-26 13:34
閱讀 2385·2019-08-26 10:35
閱讀 605·2019-08-23 17:57
閱讀 2029·2019-08-23 17:15