Java 8 引入 Lambda 表達式 (JSR-335) 與 默認方法,標誌著 Java 正式擁抱函數式編程範式。默認方法允許在接口中提供方法的預設實現,為現有集合 API(List、Map 等)平滑演進提供了可能,同時也帶來了類似多重繼承的能力。本文將深入剖析 Lambda 語法、方法引用、構造器引用、默認方法機制,並探討基於默認方法的 Mixin 模式實現及其陷阱。
| 概念 | 說明 |
|---|---|
| Functional Interface | 僅包含一個抽象方法的接口(如 Runnable、Comparator),可隱式轉換為 Lambda |
| Lambda 表達式 | 匿名函數語法糖:(params) -> body,解決匿名內部類冗長、this 指向混淆等問題 |
| 方法引用 | ClassName::methodName 或 instance::methodName,將現有方法作為 Functional Interface 實例傳遞 |
| 構造器引用 | ClassName::new,將構造器作為 Functional Interface 實例 |
| 默認方法 | 接口中用 default 修飾的非抽象方法,實現類可選擇覆寫或繼承 |
(String s) -> s.length() // 單參數,單表達式,隱式返回
() -> 43 // 無參數,返回常量
(int x, int y) -> x + y // 多參數,單表達式
(String s) -> { // 代碼塊體,需顯式 return
System.out.println(s);
return s.length();
}
Comparator c = (s1, s2) -> s1.compareToIgnoreCase(s2); ActionListener l = e -> e.getWhen();returnLambda 可捕獲最終語義的局部變量(不必顯式 final),但不可在 Lambda 內聲明同名變量遮蔽外部變量(編譯錯誤)。this 指向外部類實例,解決了匿名內部類的 this 混淆問題。
Arrays.sort(employees, Employee::myCompare); // Employee.myCompare(Employee, Employee)
編譯器根據目標類型 Comparator<Employee> 自動匹配參數類型兼容的靜態方法。
String::length // 等價於 s -> s.length(),String 為類名但實際指向實例方法
針對基本類型包裝類、String 等不可變類型,允許 TypeName::instanceMethod 形式。
Supplier<MyClass> supplier = MyClass::new; // 無參構造器
Function<String, Employee> factory = Employee::new; // 單參構造器
函數式接口的方法簽名決定匹配哪個構造器重載。
interface NormalInterface {
void normalMethod();
default void defaultMethod() { System.out.println("default"); }
}
InterfaceA.super.defaultMethod()interface SwitchableMixin {
default boolean isActivated() { return Switchables.isActivated(this); }
default void setActivated(boolean b) { Switchables.setActivated(this, b); }
}
class Switchables {
private static final Map<SwitchableMixin, SwitchableDeviceState> STATES = new HashMap<>();
// ... 狀態讀寫邏輯
}
缺陷:非線程安全、內存洩漏風險(Map 強引用 Key 導致無法 GC)、依賴 hashCode/equals 正確性。
interface FakeBrokenMixin {
static Map<FakeBrokenMixin, String> map = new WeakHashMap<>();
default String getName() { return map.get(this); }
default void setName(String n) { map.put(this, n); }
}
// Lambda 實例可能被緩存復用,導致狀態共享錯誤
JDK 8 編譯器將 Lambda 轉換為 invokedynamic 指令,運行時通過 LambdaMetafactory 生成實例。早期構建中 Lambda 實例可能被緩存為單例,導致狀態異常。現代 JDK 已修復,但仍建議避免在默認方法中維護可變狀態。
interface SwitchableView extends Switchable {
Switchable getSwitchable(); // 抽象 getter
default boolean isActive() { return getSwitchable().isActive(); }
default void setActive(boolean a) { getSwitchable().setActive(a); }
}
class DeviceA implements SwitchableView {
private final Switchable switchable = new SwitchableImpl();
public Switchable getSwitchable() { return switchable; }
}
優勢:線程安全、無內存洩漏、編譯期類型檢查、職責清晰分離。
::)進一步減少樣板代碼