Lambda 表達式以及方法引用 Java 8 的新特性筆記,重點講的是: Lambda 函數式介面 方法引用 Steam 流 Lambda 表達式 Lambda 的基礎使用不記錄,記錄 JDK 8 實戰 書上的一些底層和核心筆記。 行為參數化 一個貫徹 Lambda 表達式的一個模式、編程規範。 ...
Lambda 表達式以及方法引用
Java 8 的新特性筆記,重點講的是:
- Lambda 函數式介面
- 方法引用
- Steam 流
Lambda 表達式
Lambda 的基礎使用不記錄,記錄 JDK 8 實戰
書上的一些底層和核心筆記。
行為參數化
一個貫徹 Lambda 表達式的一個模式、編程規範。
語句拆解:
- 行為:就是一個方法或函數
- 參數化:將原來的具體行為,變成一個參數,這個行為就是可變的,適用於多種場景。
主要的思想是:
eg:在某一個業務場景中,有一段業務代碼,可能需要對一個參數進行一些主要的篩選判斷,最後符合條件的才能添加到列表中。
// 過濾蘋果的主要方法
// Predicate 比較
public static List<Apple> filterApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory){
if(條件篩選){ // 是否符合被篩選的條件
result.add(apple);
}
}
return result;
}
通過上面這段代碼,可以看到,這段代碼的核心處理起始只有條件篩選這塊,通過篩選後最後返回一個 boolean
值,為 ture
才能將蘋果添加到列表中最終返回。
所以條件篩選的可能有許多不同的具體實現,但是毋庸置疑的是最終都需要返回一個 boolean
值來決定是否滿足將蘋果添加至列表的操作。
所以我們將條件篩選這個行為參數化,來優化代碼,並使其更好擴展和維護。具體的實現可以直接百度 行為參數化
的詳細介紹。
主要是將這個行為參數化的目的。
Java 8 內置的一些常用函數式介面:
大部分日常能用到的在:java.util.function
包下。
註意:函數式介面一般會在類上加上註解 @FunctionalInterface
標識這個是函數式介面。
Lambda 表達式的使用前提是函數式介面。
Lambda 的類型檢查、類型推斷以及限制
類型檢查:
類型檢查很好理解,根據使用 Lambda 的上下文(例如,接受它傳遞的方法的參數,或者接受它的值的局部變數)推斷出來的,上下文中使用 Lambda 的類型稱為 目標類型 (就是使用哪個具體的類使用Lambda),根據目標類型(肯定是一個函數式介面)中的抽象方法簽名來對應 Lambda 表達式的簽名(包括抽象方法中聲明的 throws 異常),檢查這個 Lambda 表達式是否符合規範。
特殊的 void 相容規則:
如果一個Lambda的主體是一個語句表達式, 它就和一個返回 void 的函數描述符相容(當然需要參數列表也相容)。
eg:以下兩行都是合法的,儘管 List 的 add 方法返回了一個boolean ,而不是 Consumer 上下文( T -> void )所要求的 void。
List<String> stringList = new ArrayList<>();
// Predicate返回了一個boolean
Predicate<String> predicate = s -> stringList.add(s);
// Consumer返回了一個void
Consumer<String> consumer = s -> stringList.add(s);
類型推斷:
使用 Lambda 表達式,如果至少有一個參數,則可以顯示的說明參數的具體類型,同時也可以不顯示說明,只寫形參名,則會觸發 Java 編譯器的類型推斷機制,根據上下文來推斷 Lambda 表達式的參數類型。
註意:如果只有一個參數還可以省去參數兩邊的括弧。
限制:使用局部變數
Lambda 表達式允許使用自由變數(實例變數,靜態變數)以及局部變數,但是在使用局部變數時有限制。
我們必須保證 Lambda 表達式引用的局部變數最終是 final
,也就是說使用的是局部變數的副本,而不是直接去訪問原始變數。
因為局部變數存在棧中,這個和 Lambda 執行時線程安全相關。
在使用實例變數時,可以改變賦值(引用地址),因為這些變數保存在堆中,而堆是線上程之間共用的
方法引用
Java 8 的另一個功能:方法引用,某些 Lambda 的快捷寫法。
基本思想:如果 Lambda 代表的只是 “直接調用這個方法”,可以直接用名稱調用它,而不是去描述如何調用。
使用方法:目標引用 :: 方法名稱
複合 Lambda 表達式的有用方法
例如:Comparator、Function 和 Predicate 都提供了允許你進行複合的方法。
比較器複合(Comparator)
List<Apple> inventory = Arrays.asList(Apple...);
-
逆序
inventory.sort(Comparator.comparing(Apple::getWeight).reversed());
-
比較器鏈:thenComparing() 如果一樣,則按照指定的規則排序
inventory.sort(Comparator.comparing(Apple::getWeight).thenComparing(Apple::getColor));
謂詞複合(Predicate)
介面包含三個預設方法:negate、and 和 or
,重用已有的 Predicate 來創建更複雜的謂詞。
redApple
是現有的 Predicate
的實例。
-
negate:
表示此謂詞的邏輯否定的謂詞。
Predicate<Apple> notRedApple = redApple.negate();
-- > 蘋果不是紅色的 -
and:
該謂詞表示此謂詞與另一個謂詞的短路邏輯AND。在計算組合謂詞時,如果此謂詞為假,則不計算其他謂詞。
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);
-- > 蘋果又紅又重。 -
or:
一個組合謂詞,該謂詞表示此謂詞與另一個謂詞的短路邏輯或。在計算組合謂詞時,如果此謂詞為真,則不計算其他謂詞。
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getColor()));
-- > 要麼蘋果又紅又重,要麼就是綠蘋果。
函數複合(Function)
compose()
andThen()
兩者都是函數的複合,其主要區別是:
- compose:
f.compose(g)
,先調用 g 再調用 f - andThen:
f.andThen(g)
,先調用 f 在調用 g
可以用作流水線模式。