Lambda 表達式是 Java 8 最具革命性的特性,標誌著 Java 正式引入函數式編程風格。它不僅簡化了匿名內部類的冗長語法,更配合 Stream API 實現了集合的「內部迭代」與並行處理。本文系統講解 Lambda 語法、目標類型推斷、核心函數式接口、Stream 流操作模式、變量捕獲、方法引用及默認方法等核心知識點。
Lambda 本質是匿名方法,語法結構:(參數列表) -> { 方法體 } 或 (參數列表) -> 表達式。
// 無參無返回 (Runnable)
() -> System.out.println("Hello Lambda")
// 單參數,類型可推斷,單行表達式 (Function)
s -> s.length()
// 多參數,顯式類型,代碼塊帶返回
(int x, int y) -> { return x + y; }
// 多參數,類型推斷,表達式體 (隱式返回)
(x, y) -> x + y
->:分隔參數與主體。{ ... } 代碼塊(需显式 return)。Lambda 無獨立類型,其類型由上下文推斷,稱為「目標類型」。目標類型必須是函數式接口:有且僅有一個抽象方法的接口(可用 @FunctionalInterface 標註)。
| 接口 | 抽象方法 | 語義 | 典型場景 |
|---|---|---|---|
Predicate<T> | boolean test(T t) | 斷言/過濾 | stream.filter() |
Function<T, R> | R apply(T t) | 轉換/映射 | stream.map() |
Consumer<T> | void accept(T t) | 消費/處理 | stream.forEach() |
Supplier<T> | T get() | 供給/生成 | Stream.generate() |
此外還有 Operator (一元/二元運算)、Comparator、Runnable、Callable 等。
c = () -> "Result"; // 目標類型 Callable Comparatorcmp = (a, b) -> a - b; // 目標類型 Comparator // 錯誤:Object 不是函數式接口 // Object o = () -> {}; // Compile Error // 必須顯式轉型 Object o = (Runnable) () -> {}; // OK
Stream API 實現集合的內部迭代(由庫控制迭代),支持鏈式操作(Pipeline)、懶加載與並行計算。
Listlist = Arrays.asList("a", "b", "c"); Stream s1 = list.stream(); // 串行流 Stream s2 = list.parallelStream(); // 並行流 Stream s3 = Stream.of("a", "b", "c"); Stream s4 = Stream.iterate(0, n -> n + 2).limit(5); // 0,2,4,6,8
list.stream()
.filter(s -> s.length() > 3) // Predicate 過濾
.map(String::toUpperCase) // Function 映射 (方法引用)
.distinct() // 去重
.sorted() // 排序
.peek(s -> System.out.println(s)) // 側寫調試
.limit(10) // 截取
.skip(2); // 跳過
// 收集 Listresult = stream.collect(Collectors.toList()); Set set = stream.collect(Collectors.toSet()); Map map = stream.collect(Collectors.toMap(String::length, s -> s)); // 歸約 Optional max = stream.max(Comparator.naturalOrder()); int sum = stream.mapToInt(String::length).sum(); // 原始流優化 // 遍歷/匹配 stream.forEach(System.out::println); boolean any = stream.anyMatch(s -> s.startsWith("a")); boolean all = stream.allMatch(s -> s.length() > 0);
// 需求:從字符串列表中提取不重複的素數並求和 Listinputs = Arrays.asList("2", "3", "4", "5", "5", "7", "11", "abc"); int sum = inputs.stream() .map(Integer::valueOf) // String -> Integer (Function) .filter(Primes::isPrime) // Predicate 過濾素數 .distinct() // 去重 .reduce(0, Integer::sum); // Reduce 求和 (BinaryOperator) System.out.println("Distinct Prime Sum: " + sum); // 2+3+5+7+11 = 28
Collectors.groupingBy)// 統計不同年齡段人數 MapageGroupCount = persons.parallelStream() // 並行 .collect(Collectors.groupingBy( p -> p.getAge() < 18 ? "Minor" : "Adult", Collectors.counting() )); // 多級分組:性別 -> 年齡段 -> 列表 Map >> multi = persons.stream() .collect(Collectors.groupingBy( Person::getSex, Collectors.groupingBy(p -> p.getAge() > 30 ? "Senior" : "Junior") ));
Lambda 可訪問外部局部變量,但變量必須是 final 或 effectively final(僅賦值一次)。這解決了多線程下的共享可變狀態問題。
int factor = 2; // effectively final Functionmultiplier = x -> x * factor; // factor = 3; // 編譯錯誤:不再 effectively final // 實例字段、靜態字段無限制 this.instanceField = 10; Function f = x -> x + this.instanceField; // 捕獲 this
當 Lambda 僅調用現有方法時,用方法引用更簡潔:類名::方法名 或 對象::方法名。
// 靜態方法引用 Functionparser = Integer::valueOf; // s -> Integer.valueOf(s) // 實例方法引用 (特定對象) Consumer printer = System.out::println; // s -> System.out.println(s) // 實例方法引用 (任意對象) - 參數成為調用者 Comparator cmp = String::compareToIgnoreCase; // (s1,s2) -> s1.compareToIgnoreCase(s2) // 構造器引用 Supplier > listFactory = ArrayList::new; // () -> new ArrayList() Function
userCreator = User::new; // name -> new User(name) // 數組構造器引用 IntFunction arrayMaker = String[]::new; // len -> new String[len]
Java 8 允許接口定義默認實現 (default) 和靜態方法,解決接口擴展兼容性問題(如 Collection.stream())。
interface MyInterface {
void abstractMethod();
default void defaultMethod() { // 默認實現
System.out.println("Default impl");
}
static void staticMethod() { // 靜態工具方法
System.out.println("Static helper");
}
}
菱形繼承衝突:實現類必須顯式覆蓋並選擇調用 SuperInterface.super.defaultMethod()。
// 無限隨機數流,需配合 limit() 短路
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
// 迭代生成斐波那契數列
Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0]+t[1]})
.limit(10)
.map(t -> t[0])
.forEach(System.out::println);
list.forEach(System.out::println))。map/filter 中修改外部狀態。parallelStream(),否則上下文切換開銷得不償失。IntStream/LongStream/DoubleStream 避免裝箱拆箱開銷。try-catch 或自定義函數式接口。Lambda 表達式 + Stream API 徹底改變了 Java 集合處理方式,從「外部迭代、命令式」轉向「內部迭代、聲明式、可並行」。掌握四大核心函數式接口、中間/終端操作分類、方法引用、變量捕獲規則,即可編寫優雅、高效、現代化的 Java 代碼。這也是進階學習 Reactive Streams (Reactor/RxJava)、並行計算 (ForkJoin/CompletableFuture) 的基石。