Java8 的一個(gè)大亮點(diǎn)是引入 Lambda 表達(dá)式,使用它設(shè)計(jì)的代碼會更加簡潔。當(dāng)開發(fā)者在編寫 Lambda 表達(dá)式時(shí),也會隨之被編譯成一個(gè)函數(shù)式接口。
OSCHINA 本期高手問答 (8 月 23 日 - 8 月 29 日) 我們請來了嘉賓?阿超老師?來和大家一起探討關(guān)于 Lambda 和 Stream 的問題,將以【如何使用 lambda 表達(dá)式提升開發(fā)效率】為切入點(diǎn)展開討論。
可討論的問題包括但不限于:
lambda 表達(dá)式的應(yīng)用場景
Stream 的應(yīng)用場景
Lambda/Stream 的進(jìn)一步封裝
除了上述三個(gè)范圍,你也可以將討論的內(nèi)容外延到函數(shù)式編程的整個(gè)領(lǐng)域(不限于編程語言),包括各大開源項(xiàng)目中對其的封裝、應(yīng)用等等,還可以專注于開源的 orm 框架 Mybatis-Plus 的源碼、實(shí)踐等細(xì)節(jié)。
嘉賓介紹
阿超,00 后全棧開發(fā),dromara 組織成員、hutool 團(tuán)隊(duì)成員、mybatis-plus 團(tuán)隊(duì)成員、stream-query 項(xiàng)目作者,參與貢獻(xiàn)的開源項(xiàng)目包括不限于 apache-shenyu、apache-streampark 等。
為了鼓勵踴躍提問,問答結(jié)束后我們會從提問者中抽取 5 名幸運(yùn)會員,贈予?開源項(xiàng)目 stream-query 的開源周邊 T 恤,由阿超親自設(shè)計(jì)!
Lambda 表達(dá)式
簡單來說:就是把我們的函數(shù) (方法) 作為參數(shù)傳遞、調(diào)用等
例子:自定義函數(shù)式接口(用?jdk?自帶的函數(shù)式接口也可以)
?
import java.io.Serializable; /** * 可序列化的Functional * * @author VampireAchao */ @FunctionalInterface public interfaceFuncextendsSerializable{ /** * 調(diào)用 * * @param t 參數(shù) * @return 返回值 */ R apply(T t); }
?
我們定義一個(gè)類可以去實(shí)現(xiàn)該接口
?
/** * 可序列化的函數(shù)式接口實(shí)現(xiàn)類 * * @author VampireAchao */ public classFuncImplimplementsFunc到此為止,都非常的簡單 這里就有個(gè)問題:假設(shè)我有很多的地方需要不同的類去實(shí)現(xiàn)?Func,我就得每次都去寫這么一個(gè)類,然后實(shí)現(xiàn)該接口并重寫方法 這樣很麻煩!因此我們使用匿名內(nèi)部類
Func我們可以看到,使用了匿名內(nèi)部類后不用每次去新建這個(gè)類了,只需要在調(diào)用的地方,new?一下接口,創(chuàng)建一個(gè)匿名內(nèi)部類即可 但這樣還有個(gè)問題,我們每次都要寫這么一大幾行代碼,特別麻煩 由此而生,我們有了?lambda?這種簡寫的形式 https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#syntaxfunc = new Func () { /** * 調(diào)用 * * @param s 參數(shù) * @return 返回值 */ @Override public Integer apply(String s) { return s.hashCode(); } };
Func如果只有一行,我們可以省略掉中括號以及?returnfunc1 = (String s) -> { return s.toUpperCase(); };
Funcfunc2 = (String s) -> s.toUpperCase();
?
我們可以省略掉后邊的參數(shù)類型
?
Funcfunc3 = s -> s.toUpperCase();
?
如果我們滿足特定的形式,我們還可以使用方法引用(雙冒號)的形式縮寫
?
Func這里除了我們的參數(shù)->返回值寫法:s->s.toUpperCase(),還有很多種 例如無參數(shù)帶返回值寫法?()->"yes"、無參無返回值寫法?()->{}?等等 而方法引用這種寫法有如下幾種: https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.htmlfunc4 = String::toUpperCase;
package org.dromara.streamquery; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; /** * 語法糖——方法引用 * * @author VampireAchao */ public class MethodReferences { public static Object staticSupplier() { return "whatever"; } public Object instanceSupplier() { return "whatever"; } public Object anonymousInstanceFunction() { return "whatever"; } public static void main(String[] args) { // 引用構(gòu)造函數(shù) SupplierconSup = () -> new MethodReferences(); conSup = MethodReferences::new; // 數(shù)組構(gòu)造函數(shù)引用 IntFunction intFunction = value -> new int[value]; // intFunc == new int[20]; int[] intFuncResult = intFunction.apply(20); // 引用靜態(tài)方法 Supplier
?
順便放幾個(gè)常用的,jdk?自帶的函數(shù)式接口寫法
?
package org.dromara.streamquery; import java.math.BigDecimal; import java.util.function.*; /** * 常用的幾個(gè)函數(shù)式接口寫法 * * @author VampireAchao */ class Usual { public static Consumerconsumer() { // 有參數(shù)無返回值 return o -> { }; } public static Function function() { // 有參數(shù)有返回值 return o -> o; } public static Predicate predicate() { // 有參數(shù),返回值為boolean return o -> true; } public static Supplier supplier() { // 無參數(shù)有返回值 return Object::new; } public static BiConsumer biConsumer() { // 倆參數(shù)無返回值 return (q, o) -> { }; } public static BiFunction biFunction() { // 倆參數(shù),有返回值 return (q, o) -> new BigDecimal(q).add(BigDecimal.valueOf(o)); } public static UnaryOperator unaryOperator() { // 一個(gè)參數(shù),返回值類型和參數(shù)一樣 return q -> q; } public static BinaryOperator binaryOperator() { // 倆參數(shù)和返回值類型保持一致 return (a, o) -> a; } }
?
Stream
Java 8 API?添加了一個(gè)新的抽象稱為流?Stream,可以讓你以一種聲明的方式處理數(shù)據(jù)。方法全是傳入函數(shù)作為參數(shù),來達(dá)到我們的目的。 https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
// 聲明式編程是告訴計(jì)算機(jī)需要計(jì)算“什么”而不是“如何”去計(jì)算 // 現(xiàn)在,我想要一個(gè)List,包含3個(gè)數(shù)字6 ListStream?使用一種類似用?SQL?語句從數(shù)據(jù)庫查詢數(shù)據(jù)的直觀方式來提供一種對?Java?集合運(yùn)算和表達(dá)的高階抽象。sixSixSix = // 我想要: Stream // 數(shù)字6 .generate(() -> 6) // 3個(gè) .limit(3) // 最后收集起來轉(zhuǎn)為List .collect(Collectors.toList()); sixSixSix.forEach(System.out::print);
// 就像sql里的排序、截取 // 我要把傳入的list逆序,然后從第五個(gè)(元素下標(biāo)為4)開始取值,取4條 abc = abc.stream() // 排序(按照自然順序的逆序) .sorted(Comparator.reverseOrder()) // 從下標(biāo)為4開始取值 .skip(4) // 取4條 .limit(4) // 最后收集起來轉(zhuǎn)為List .collect(Collectors.toList()); System.out.println("我要把傳入的list逆序,然后從第五個(gè)(元素下標(biāo)為4)開始取值,取4條"); abc.forEach(System.out::print); System.out.println();Stream API?可以極大提高?Java?程序員的生產(chǎn)力,讓程序員寫出高效率、干凈、簡潔的代碼。
/** * 老辦法實(shí)現(xiàn)一個(gè)list,存儲3個(gè)6 * * @return [6, 6, 6] */ private static ListoldSix() { // 老辦法 List sixSixSix = new ArrayList<>(3); sixSixSix.add(6); sixSixSix.add(6); sixSixSix.add(6); System.out.println("老辦法實(shí)現(xiàn)一個(gè)list,存儲3個(gè)6"); for (Integer integer : sixSixSix) { System.out.print(integer); } System.out.println(); return sixSixSix; } /** * 新方法實(shí)現(xiàn)一個(gè)list,存儲3個(gè)6 * * @return [6, 6, 6] */ private static List newSix() { List sixSixSix = Stream.generate(() -> 6).limit(3).collect(Collectors.toList()); System.out.println("新方法實(shí)現(xiàn)一個(gè)list,存儲3個(gè)6"); sixSixSix.forEach(System.out::print); System.out.println(); return sixSixSix; }
?
這種風(fēng)格將要處理的元素集合看作一種流, 流在管道中傳輸, 并且可以在管道的節(jié)點(diǎn)上進(jìn)行處理, 比如篩選, 排序,聚合等。
?
// 管道中傳輸,節(jié)點(diǎn)中處理 int pipe = abc.stream() // 篩選 .filter(i -> i > 'G') // 排序 .sorted(Comparator.reverseOrder()) .mapToInt(Object::hashCode) // 聚合 .sum(); System.out.println("將26個(gè)字母組成的集合過濾出大于'G'的,逆序,再獲取hashCode值,進(jìn)行求和"); System.out.println(pipe);元素流在管道中經(jīng)過中間操作(intermediate operation)的處理,最后由最終操作 (terminal operation) 得到前面處理的結(jié)果。
// 將26個(gè)大寫字母Character集合轉(zhuǎn)換為String然后轉(zhuǎn)換為小寫字符 ListterminalOperation = abc.stream() // 中間操作(intermediate operation) .map(String::valueOf).map(String::toLowerCase) // 最終操作(terminal operation) .collect(Collectors.toList()); System.out.println("26個(gè)大寫字母Character集合,轉(zhuǎn)換成String然后轉(zhuǎn)換為小寫字符,收集起來"); terminalOperation.forEach(System.out::print); System.out.println();
?
評論
查看更多