十年網(wǎng)站開(kāi)發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專(zhuān)業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
在Java 8之前,我們通常會(huì)為每種需要封裝單個(gè)功能的情況創(chuàng)建一個(gè)類(lèi),這意味著需要大量不必要的樣板代碼。

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),漣源企業(yè)網(wǎng)站建設(shè),漣源品牌網(wǎng)站建設(shè),網(wǎng)站定制,漣源網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,漣源網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
Java 8以Lambda表達(dá)式的形式帶來(lái)了一個(gè)強(qiáng)大的新語(yǔ)法改進(jìn),Lambda是一個(gè)匿名函數(shù)。
建議所有函數(shù)接口都使用@FunctionalInterface注解,用于清楚傳達(dá)函數(shù)接口的目的,并且還允許編譯器在帶有該注解的接口,在不滿足條件的情況下生成編譯錯(cuò)誤。
任何帶有SAM(單一抽象方法)的接口都是函數(shù)接口,被視為L(zhǎng)ambda表達(dá)式。
Java8 的默認(rèn)方法不是抽象的,也不算在內(nèi),函數(shù)接口允許存在多個(gè)默認(rèn)方法。
Lambda最簡(jiǎn)單、最通用的情況是一個(gè)函數(shù)接口,該接口具有一個(gè)接收一個(gè)值并返回另一個(gè)值的方法。單個(gè)參數(shù)的函數(shù)可以由Function接口表示,該接口通過(guò)其參數(shù)的類(lèi)型和返回值進(jìn)行參數(shù)化:
public interface Function { … } Function類(lèi)型在標(biāo)準(zhǔn)JDK庫(kù)中的用法之一是Map.computeIfAbsent方法。此方法按鍵返回映射中的值,但如果映射中尚未存在鍵,則會(huì)計(jì)算值。要計(jì)算一個(gè)值,它使用傳遞的Function實(shí)現(xiàn):
Map nameMap = new HashMap<>();
Integer value = nameMap.computeIfAbsent("John", s -> s.length()); Function接口還有一個(gè)默認(rèn)的compose方法,它允許我們將多個(gè)函數(shù)組合為一個(gè)函數(shù)并按順序執(zhí)行:
Function intToString = Object::toString;
Function quote = s -> "'" + s + "'";
Function quoteIntToString = quote.compose(intToString);
assertEquals("'5'", quoteIntToString.apply(5)); 由于基元類(lèi)型不能是泛型類(lèi)型參數(shù),因此對(duì)于最常用的基元類(lèi)型double、int、long及其在參數(shù)和返回類(lèi)型中的組合,函數(shù)接口有以下版本:
例如,對(duì)于一個(gè)使用short并返回字節(jié)的函數(shù):
@FunctionalInterface
public interface ShortToByteFunction {
byte applyAsByte(short s);
}現(xiàn)在,我們可以編寫(xiě)一個(gè)方法,使用ShortToByteFunction定義的規(guī)則將short數(shù)組轉(zhuǎn)換為字節(jié)數(shù)組:
public byte[] transformArray(short[] array, ShortToByteFunction function) {
byte[] transformedArray = new byte[array.length];
for (int i = 0; i < array.length; i++) {
transformedArray[i] = function.applyAsByte(array[i]);
}
return transformedArray;
}以下是我們?nèi)绾问褂盟鼘hort數(shù)組轉(zhuǎn)換為字節(jié)乘以2的數(shù)組:
short[] array = {(short) 1, (short) 2, (short) 3};
byte[] transformedArray = transformArray(array, s -> (byte) (s * 2));
byte[] expectedArray = {(byte) 2, (byte) 4, (byte) 6};
assertArrayEquals(expectedArray, transformedArray);要用兩個(gè)參數(shù)定義lambda,我們必須使用名稱(chēng)中包含“Bi”關(guān)鍵字的附加接口:BiFunction、ToDoubleBiFunction、ToIntBiFunction和ToLongBiFunction。
BiFunction同時(shí)生成了參數(shù)和返回類(lèi)型,而ToDoubleBiFunction和其他函數(shù)允許我們返回基元值。
在標(biāo)準(zhǔn)API中使用此接口的典型示例之一是Map.replaceAll方法,它允許用一些計(jì)算值替換Map中的所有值。
讓我們使用一個(gè)BiFunction實(shí)現(xiàn),該實(shí)現(xiàn)接收一個(gè)鍵和一個(gè)舊值來(lái)計(jì)算工資的新值并返回。
Map salaries = new HashMap<>();
salaries.put("John", 40000);
salaries.put("Freddy", 30000);
salaries.put("Samuel", 50000);
salaries.replaceAll((name, oldValue) ->
name.equals("Freddy") ? oldValue : oldValue + 10000); 通常用它來(lái)提供數(shù)據(jù)產(chǎn)出,例如,讓我們定義一個(gè)函數(shù),它將一個(gè)值平方:
public double squareLazy(Supplier lazyValue) {
return Math.pow(lazyValue.get(), 2);
}
Supplier lazyValue = () -> {
Uninterruptibles.sleepUninterruptibly(1000, TimeUnit.MILLISECONDS);
return 9d;
};
Double valueSquared = squareLazy(lazyValue); 讓我們使用一個(gè)靜態(tài)Stream.generate方法來(lái)創(chuàng)建一個(gè)Fibonacci數(shù)字流:
int[] fibs = {0, 1};
Stream fibonacci = Stream.generate(() -> {
int result = fibs[1];
int fib3 = fibs[0] + fibs[1];
fibs[0] = fibs[1];
fibs[1] = fib3;
return result;
}); 我們使用一個(gè)數(shù)組而不是兩個(gè)變量,因?yàn)閘ambda內(nèi)部使用的所有外部變量都必須是有效的final。
例如,讓我們通過(guò)在控制臺(tái)中打印問(wèn)候語(yǔ)來(lái)問(wèn)候姓名列表中的每個(gè)人。傳遞給List.forEach方法的lambda實(shí)現(xiàn)了Consumer函數(shù)接口:
List names = Arrays.asList("John", "Freddy", "Samuel");
names.forEach(name -> System.out.println("Hello, " + name)); 還有專(zhuān)門(mén)版本的Consumer(DoubleConsumer、IntConsumer和LongConsumer),它們接收基元值作為參數(shù):
Map ages = new HashMap<>();
ages.put("John", 25);
ages.put("Freddy", 24);
ages.put("Samuel", 30);
ages.forEach((name, age) -> System.out.println(name + " is " + age + " years old")); 另一組專(zhuān)門(mén)的BiConsumer版本由ObjDoubleConsumer、ObjIntConsumer和ObjLongConsumer組成,它們接收兩個(gè)參數(shù);其中一個(gè)參數(shù)是泛型的,另一個(gè)是基元類(lèi)型。
Predicates是一個(gè)接收值并返回布爾值的函數(shù)。
List names = Arrays.asList("Angela", "Aaron", "Bob", "Claire", "David");
List namesWithA = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList()); 在上面的代碼中,我們使用流API過(guò)濾列表,并只保留以字母“a”開(kāi)頭的名稱(chēng)。Predicates實(shí)現(xiàn)封裝了篩選邏輯。
與前面的所有示例一樣,此函數(shù)的IntPredicate、DoublePredicate和LongPredicate版本都接收基元值。
Operator接口是接收和返回相同值類(lèi)型的函數(shù)的特殊情況。UnaryOperator接口接收一個(gè)參數(shù)。它在集合API中的一個(gè)用例是用相同類(lèi)型的一些計(jì)算值替換列表中的所有值:
List names = Arrays.asList("bob", "josh", "megan");
names.replaceAll(name -> name.toUpperCase()); 當(dāng)然,我們可以簡(jiǎn)單地使用方法引用來(lái)代替name->name.toUpperCase():
names.replaceAll(String::toUpperCase);BinaryOperator最有趣的用例之一是歸約運(yùn)算。假設(shè)我們想將一組整數(shù)聚合為所有值的總和。使用流API,我們可以使用收集器來(lái)實(shí)現(xiàn)這一點(diǎn),但更通用的方法是使用reduce方法:
List values = Arrays.asList(3, 5, 8, 9, 12);
int sum = values.stream()
.reduce(0, (i1, i2) -> i1 + i2); reduce方法接收一個(gè)初始累加器值和一個(gè)BinaryOperator函數(shù)。此函數(shù)的參數(shù)是一對(duì)相同類(lèi)型的值;函數(shù)本身還包含一個(gè)邏輯,用于將它們連接到同一類(lèi)型的單個(gè)值中。傳遞的函數(shù)必須是關(guān)聯(lián)的,這意味著值聚合的順序無(wú)關(guān)緊要,即應(yīng)滿足以下條件:
op.apply(a, op.apply(b, c)) == op.apply(op.apply(a, b), c)并不是所有的功能接口都出現(xiàn)在Java 8中。以前版本的Java中的許多接口都符合FunctionalInterface的約束,我們可以將它們用作lambda。
突出的例子包括并發(fā)API中使用的Runnable和Callable接口。在Java 8中,這些接口也用@FunctionalInterface注釋進(jìn)行標(biāo)記。這使我們能夠極大地簡(jiǎn)化并發(fā)代碼:
Thread thread = new Thread(() -> System.out.println("Hello From Another Thread"));
thread.start();本文主要演示了Java 8 API中的不同功能的函數(shù)接口,這些接口可以用作Lambda表達(dá)式。函數(shù)式流式編程方法在Java 8 之后的項(xiàng)目中應(yīng)用非常普遍。