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



















評論