Oracle 釋出的 Java 7 原型實現提供兩種寫法:
#int() func1 = #()(3);
#int(int) func2 = #(int x)(x + 1);
#int(int, int) func3 = #(int x, int y)(x - y);
#int() func1 = #{ return 3; };
#int(int) func2 = #(int x){ return x + 1; };
#int(int, int) func3 = #(int x, int y){ return x - y; };
| 特性 | Java 7 | C# |
|---|---|---|
| 參數/主體分隔 | 無 | => |
| 單一運算式括號 | 必要 | 可省略 |
| 單參數括號 | 必要 | 可省略 |
| 類型推論 | 僅回傳值與異常 | 參數、回傳皆支援 |
範例:對使用者陣列按年齡排序
// C#: users.Sort(u => u.Age);
// Java: Arrays.sort(users, #(User u)(u.Age));
C# 更具宣告式可讀性;Java 顯得冗贅。
Java 允許 Lambda 直接轉換為具單一抽象方法的介面(如 Func),無需預先定義 Delegate 類型,較 C# 靈活:
Func[] arr = new Func[10];
for(int i=0;i
閉包捕獲語義與 this 歧義
Lambda 內部存取變數優先順序:目標 SAM 類型成員 > 詞法作用域成員。範例:
public class MyClass {
public int n = 3;
public void m() {
Func f = #(int x)(x + n); // 捕獲 MyClass.n
}
public int hashCode() { throw new RuntimeException(); }
public void test() {
Func f = #(int x)(x * hashCode()); // 呼叫 Func.hashCode(),不拋出
// 需寫 MyClass.this.hashCode() 才呼叫外部方法
}
}
此設計雖相容既有 SAM API,但與主流語言詞法捕獲語義不同,易產生困惑。
硬傷:Checked Exception 對 Lambda 的衝擊
Java 受檢異常強制方法宣告 throws,導致函數式介面難以通用:
// 無法編譯:getCanonicalPath() 拋 IOException
map(files, #(String f)(new File(f).getCanonicalPath()));
若介面不宣告 throws IOException,Lambda 無法拋出;若宣告,則介面綁定特定異常,失去通用性。Java 雖支援異常類型推論,但根本衝突未解。Scala/Groovy 棄用 Checked Exception 即為佐證。
其他特性
- 參數泛化:
#boolean(Integer) f = #(Number n)(n.intValue() > 0);
- 與
MethodHandle 互轉。
結語
Java 7 Lambda 在 SAM 相容性上有實用價值,但語法冗長、缺乏參數類型推論、Checked Exception 成為函數式編程絆腳石,整體優雅度不及 C#/Scala。