Lambda 表達式是 Java 8 最具標誌性的特性,引入函數式編程風格,徹底改變集合操作、並發編程、回調處理等核心場景代碼風格。本文系統講解語法規則、函數式介面體系、方法引用、類型推導原理、Stream API 實戰與最佳實踐。
// 完整形式
(參數列表) -> { 語句體; return 結果; }
// 常見簡化形式
(String s) -> System.out.println(s) // 單參數、單語句、無返回
s -> System.out.println(s) // 單參數類型可推導時省略括號
(int a, int b) -> a + b // 多參數、表達式體 (隱式 return)
(a, b) -> { return a - b; } // 代碼塊體需显式 return
() -> System.out.println("Hello") // 無參數
() -> { System.out.println("Hi"); return 1; } // 無參數、代碼塊、有返回
定義:有且僅有一個抽象方法的介面,可隱式轉換為 Lambda 表達式。標註 @FunctionalInterface 可獲編譯器校驗。
| 介面 | 參數 | 返回 | 語義 | 典型場景 |
|---|---|---|---|---|
| Function<T,R> | T | R | 轉換 | 對象映射、類型轉換 |
| Predicate<T> | T | boolean | 斷言 | 過濾、條件判斷 |
| Consumer<T> | T | void | 消費 | 遍歷處理、日誌輸出 |
| Supplier<T> | 無 | T | 供給 | 延遲初始化、工廠方法 |
| UnaryOperator<T> | T | T | 一元運算 | 同類型轉換 |
| BinaryOperator<T> | T, T | T | 二元運算 | 聚合、減少操作 |
另有 BiFunction、BiPredicate、BiConsumer 等雙參數變體,以及 IntFunction、LongPredicate 等基本類型特化版本。
Lambda 僅調用單一方法時的語法糖,四大類型:
| 類型 | 語法 | 對應 Lambda | 示例 |
|---|---|---|---|
| 靜態方法引用 | 類名::靜態方法 | x -> 類名.靜態方法(x) | Integer::parseInt |
| 實例方法引用 (特定對象) | 對象::實例方法 | x -> 對象.實例方法(x) | list::add |
| 實例方法引用 (任意對象) | 類名::實例方法 | (obj, args) -> obj.實例方法(args) | String::length |
| 構造器引用 | 類名::new | args -> new 類名(args) | ArrayList::new |
| 陣列構造器引用 | 類型[]::new | len -> new 類型[len] | String[]::new |
// Lambda Functionf1 = s -> Integer.parseInt(s); Function f2 = s -> new Integer(s); Function f3 = len -> new String[len]; // 方法引用 (更簡潔、可讀性更強) Function f1 = Integer::parseInt; Function f2 = Integer::new; Function f3 = String[]::new; // 任意對象實例方法引用 List list = Arrays.asList("a", "bb", "ccc"); list.stream().map(String::length).forEach(System.out::println);
編譯器根據目標類型 (Target Type) 推導:
Function)Integer apply(String t))這就是為什麼 Lambda 不能單獨存在,必須有目標類型上下文 (賦值、傳參、返回、強轉)。
Stream 是「支援串行/並行聚合操作的元素序列」,非集合、非資料結構,而是運算管道。
filter, map, sorted, distinct, limit, skipforEach, collect, reduce, count, min/max, findFirst/AnyListemployees = Arrays.asList( new Employee("張三", "男", 25), new Employee("李四", "女", 24), new Employee("王五", "女", 23), new Employee("周六", "男", 22), new Employee("孫七", "女", 21), new Employee("劉八", "男", 20) );
// 1. 篩選 + 遍歷
employees.stream()
.filter(e -> "男".equals(e.getSex()))
.forEach(System.out::println);
// 2. 排序 (年齡升序)
employees.stream()
.sorted(Comparator.comparing(Employee::getAge))
.forEach(System.out::println);
// 3. 查找年齡最大的女員工
Employee oldestFemale = employees.stream()
.filter(e -> "女".equals(e.getSex()))
.max(Comparator.comparing(Employee::getAge))
.orElse(null);
// 4. 年齡 > 20 的男員工
employees.stream()
.filter(e -> e.getAge() > 20 && "男".equals(e.getSex()))
.forEach(System.out::println);
// 5. 年齡最大的 2 名男員工
employees.stream()
.filter(e -> "男".equals(e.getSex()))
.sorted(Comparator.comparing(Employee::getAge).reversed())
.limit(2)
.forEach(System.out::println);
// 6. 所有男員工姓名用逗號連接
String names = employees.stream()
.filter(e -> "男".equals(e.getSex()))
.map(Employee::getName)
.collect(Collectors.joining(","));
// 7. 年齡統計信息
IntSummaryStatistics stats = employees.stream()
.mapToInt(Employee::getAge)
.summaryStatistics();
System.out.println("總數:" + stats.getCount() + " 最大:" + stats.getMax() +
" 最小:" + stats.getMin() + " 平均:" + stats.getAverage());
// 並行流:自動利用多核 CPU,適合 CPU 密集型、大數據量
employees.parallelStream()
.filter(...)
.map(...)
.collect(...);
// 注意:並行流有線程池開銷,小數據量反而更慢;有狀態操作 (sorted/distinct) 並行效果有限
findFirst(), anyMatch() 可短路,避免全量處理IntStream, LongStream, DoubleStream 避免裝箱拆箱開銷Collectors.toMap(), groupingBy(), partitioningBy() 解決複雜聚合Lambda + Stream + 函數式介面構成 Java 8 函數式編程三駕馬車。它們不僅簡化代碼,更帶來聲明式編程思維:描述「做什麼」而非「怎麼做」。掌握這套體系,是現代 Java 開發者的必修課。