Java 長期堅持「一切皆對象」,函數無法獨立存在。函數式編程語言 (JS, Scala, Kotlin) 中函數是一等公民,可賦值、傳參、返回。Lambda 為 Java 引入函數值概念,使代碼更簡潔、並行處理更容易、集合操作更聲明式。
核心對比:匿名內部類 vs Lambda
// 匿名內部類 (冗長) Runnable r1 = new Runnable() { @Override public void run() { System.out.println("Hello"); } }; // Lambda (精煉) Runnable r2 = () -> System.out.println("Hello");
@FunctionalInterface
public interface MyComparator {
int compare(T a, T b); // 唯一抽象方法 (SAM)
// 默認方法、靜態方法、Object 方法不破壞函數式接口性質
default void print() { System.out.println("default"); }
static MyComparator byLength() { return (a,b) -> a.length()-b.length(); }
}
@FunctionalInterface 編譯期校驗,防止誤添加抽象方法Runnable, Callable, Comparator, ActionListenerLambda 本身無類型,其類型由目標類型上下文決定:
// 同一 Lambda 可適配不同函數式接口
Comparator c1 = (a,b) -> a-b;
MyComparator c2 = (a,b) -> a-b; // 自定義接口
// 編譯器根據賦值目標推導類型
(參數列表) -> { 方法體 }
| 形式 | 示例 | 說明 |
|---|---|---|
| 無參、表達式體 | () -> 42 | 返回 int |
| 單參、類型推導 | x -> x * x | 圓括號可省略 |
| 單參、顯式類型 | (int x) -> x * x | 需括號 |
| 多參、推導 | (x, y) -> x + y | |
| 多參、顯式類型 | (int x, int y) -> x + y | 所有參數必須同樣顯式/推導 |
| 代碼塊體、有返回 | (x, y) -> { int z = x+y; return z; } | 需 return、分號、大括號 |
| 代碼塊體、無返回 | (String s) -> { System.out.println(s); } | void 兼容 |
| 方法引用 | String::valueOf | 靜態方法引用 |
| 實例方法引用 | System.out::println | 對象::實例方法 |
| 構造器引用 | ArrayList::new | 類::new |
break/continue 跳出外層循環return 從外層方法返回 (只能從 Lambda 體返回)public class LambdaScope {
static int staticVar = 10; // 靜態變量:可讀寫
int instanceVar = 20; // 實例變量:可讀寫 (捕獲 this)
public void demo() {
int localVar = 30; // 局部變量:隱式 final,不可修改
// localVar = 40; // 編譯錯誤!
Runnable r = () -> {
System.out.println(staticVar); // OK
System.out.println(instanceVar); // OK (捕獲 this)
System.out.println(localVar); // OK (捕獲值)
staticVar++; // OK 修改靜態
instanceVar++; // OK 修改實例
// localVar++; // 錯誤:局部變量不可變
};
}
}
private static 方法,調用 invokedynamic 只創建單例實例| 接口 | 參數 | 返回 | 核心方法 | 典型用途 |
|---|---|---|---|---|
Predicate | T | boolean | test(T) | 過濾、斷言 |
Consumer | T | void | accept(T) | 消費、副作用 (打印、寫入) |
Function | T | R | apply(T) | 轉換、映射 |
Supplier | 無 | T | get() | 延遲生成、工廠 |
UnaryOperator | T | T | apply(T) | 同類型轉換 |
BinaryOperator | T,T | T | apply(T,T) | 歸約、合併 |
BiFunction | T,U | R | apply(T,U) | 雙參數轉換 |
另有 IntPredicate, LongConsumer, DoubleFunction 等基本類型特化版,避免裝箱拆箱開銷。
// Predicate 組合
Predicate nonNull = s -> s != null;
Predicate nonEmpty = s -> !s.isEmpty();
Predicate valid = nonNull.and(nonEmpty); // 且
Predicate invalid = nonNull.negate(); // 非
// Function 鏈式
Function f1 = x -> x + 1;
Function f2 = x -> x * 2;
Function f3 = f1.andThen(f2); // (x+1)*2
Function f4 = f1.compose(f2); // (x*2)+1
// Consumer 鏈式
Consumer c1 = s -> System.out.print("[");
Consumer c2 = s -> System.out.print(s);
Consumer c3 = s -> System.out.println("]");
Consumer combined = c1.andThen(c2).andThen(c3);
List names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 1. 遍歷打印
names.forEach(System.out::println); // 方法引用
// 2. 過濾長度>3 轉大寫 收集
List result = names.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.collect(Collectors.toList());
// [ALICE, CHARLIE, DAVID]
// 3. 分組統計
Map> byLength = names.stream()
.collect(Collectors.groupingBy(String::length));
// 4. 歸約求和
int totalLen = names.stream()
.mapToInt(String::length)
.sum(); // 或 .reduce(0, (a,b) -> a+b)
// 5. 查找首個
Optional firstLong = names.stream()
.filter(s -> s.length() > 5)
.findFirst(); // Optional[CHARLIE]
// 6. 並行流 (多核利用)
long count = names.parallelStream()
.filter(s -> s.length() > 3)
.count();
filter, map, flatMap, distinct, sorted, peek, limit, skip — 惰性、返回新 StreamforEach, collect, reduce, count, min/max, findFirst/any, any/all/noneMatch — 觸發計算limit, findFirst, anyMatch — 可提前終止private static 方法 (非捕獲) 或 private 實例方法 (捕獲)invokedynamic 指令點 (Call Site),而非匿名類 .class 文件invokedynamic 指令
│
▼
LambdaMetafactory.metafactory(MethodHandles.Lookup, String, MethodType, ...)
│
▼
動態生成實現目標函數式接口的類 (ASM)
│
▼
返回 CallSite,緩存後續調用直接使用
.class 文件加載/驗證| 類型 | 語法 | 等價 Lambda | 示例 |
|---|---|---|---|
| 靜態方法 | Class::staticMethod | (args) -> Class.staticMethod(args) | Integer::parseInt |
| 實例方法 (特定對象) | obj::method | (args) -> obj.method(args) | System.out::println |
| 實例方法 (任意對象) | Class::method | (obj, args) -> obj.method(args) | String::toUpperCase |
| 構造器 | Class::new | (args) -> new Class(args) | ArrayList::new |
int i=0; list.forEach(s -> i++); // 編譯錯誤 → 用 AtomicInteger 或數組 int[] cnt={0}break/continue/return 無法作用於外層循環/方法 → 改用流操作 filter/findFirst 或傳統循環list.forEach(System.out::println) 比 list.forEach(x -> System.out.println(x)) 更清晰distinct, sorted) 並行效果差IntStream, LongStream, DoubleStream 避免裝箱Optional.ofNullable(user).map(User::getAddress).map(Address::getCity).orElse("Unknown")Serializable 時才可序列化Lambda 表達式不僅是語法糖,更是 Java 向函數式編程邁出的關鍵一步。配合 Stream API、Optional、CompletableFuture,構建了現代 Java 聲明式、可組合、並行友好的編程模型。掌握其語法、類型系統、捕獲語義與運行機制,是寫出優雅、高性能 Java 代碼的必修課。