首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 函數式編程(五):閉包與詞法作用域消除 Lambda 冗余

     編輯:Java 相關知識

前言

上一篇我們用 Predicate 變量重用了固定字母的篩選邏輯。但若需「按任意字母篩選」,每個字母都寫一個 Predicate 依然冗余。本文引入高階函數、詞法作用域與閉包,實現參數化邏輯復用。

問題:字母參數化導致的冗余

Predicate<String> startsWithN = name -> name.startsWith("N");
Predicate<String> startsWithB = name -> name.startsWith("B");
// 更多字母...

邏輯完全相同,僅字母不同,違反 DRY。

方案一:靜態工廠方法 + 詞法作用域

public static Predicate<String> checkIfStartsWith(final String letter) {
    return name -> name.startsWith(letter); // 閉包:捕獲外部 letter
}
  • letter 是方法參數,屬於「實際上 final」(effectively final),Lambda 可安全捕獲。
  • 返回的 Predicate 擁有對 letter 的引用,形成閉包。

調用:

friends.stream().filter(checkIfStartsWith("N")).count();
friends.stream().filter(checkIfStartsWith("B")).count();

方案二:Function 高階函數 + 實例變量(縮小作用域)

不想污染類靜態空間,用 Function<String, Predicate<String>> 存在局部變量:

Function<String, Predicate<String>> startsWithLetter =
    letter -> name -> name.startsWith(letter); // 柯里化寫法

調用:

friends.stream().filter(startsWithLetter.apply("N")).count();
friends.stream().filter(startsWithLetter.apply("B")).count();

深度解析:詞法作用域與閉包限制

  • 只能捕獲 final 或 effectively final 變量:編譯器要求變量初始化後不再修改,否則併發下會有競態風險。
  • 無狀態 Lambda 更高效:不捕獲變量的 Lambda 運行時為常量單例(如 String::length),有捕獲則每次調用工廠方法產生新實例,有微小開銷。
  • 不要捕獲可變對象:若捕獲 int[] counter = {0} 並在 Lambda 中修改 counter[0]++,雖編譯通過,但破壞函數式純粹性,導致併發錯誤。

Predicate vs Function 選擇指南

特性Predicate<T>Function<T, R>
參數TT
返回booleanR
典型用途filter、條件判斷map、轉換、工廠方法
組合方法and/or/negateandThen/compose

總結

高階函數(返回函數的函數)配合閉包,能將「參數化邏輯」封裝為可重用的函數工廠。掌握 Function<T, Predicate<R>> 柯里化模式,是函數式編程消除冗余的核心技巧。下一篇將探討 Comparator 的函數式重構與鏈式比較器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved