Java 7 時代,給按鈕註冊點擊事件需寫匿名內部類:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TAG", "按鈕被點擊");
}
});
痛點:6 行樣板代碼只為 1 行業務邏輯,可讀性差、冗長、難維護。
Java 8 Lambda 一行解決:
button.setOnClickListener(v -> Log.d("TAG", "按鈕被點擊, Lambda"));
(參數列表) -> { 方法體 }
| 元素 | 規則 | 示例 |
|---|---|---|
| 參數列表 | 零個或多個,類型可省略 (編譯器推導),單參數可省略括號 | (), x, (int x, int y) |
| 箭頭 | -> 固定符號 | - |
| 方法體 | 單語句可省略大括號和 return;多語句需大括號並显式 return | x + 1, { System.out.println(x); return x; } |
// 無參無返回
() -> System.out.println("Hello");
// 單參無返回 (類型推導)
x -> System.out.println(x);
// 多參有返回 (隱式 return)
(x, y) -> x + y
// 多參多語句 (顯式 return)
(int x, int y) -> {
int sum = x + y;
System.out.println("計算中...");
return sum;
}
// 方法引用 (更簡潔)
System.out::println
String::length
Math::max
obj::instanceMethod
Class::staticMethod
定義:有且僅有一個抽象方法的接口 (SAM)。可標註 @FunctionalInterface 編譯期校驗。
JDK 內建四大核心函數式接口 (java.util.function):
| 接口 | 抽象方法 | 輸入 | 輸出 | 典型場景 |
|---|---|---|---|---|
Predicate<T> | boolean test(T t) | T | boolean | 過濾、判斷 |
Function<T,R> | R apply(T t) | T | R | 轉換、映射 |
Consumer<T> | void accept(T t) | T | void | 消費、打印、存儲 |
Supplier<T> | T get() | 無 | T | 工廠、延遲加載 |
另有 BiFunction、BinaryOperator、UnaryOperator 等變體。
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
// Predicate: 判斷字符串長度 > 5
Predicate longEnough = s -> s.length() > 5;
System.out.println(longEnough.test("Lambda")); // true
// Function: 字符串轉長度
Function strLen = String::length; // 方法引用
System.out.println(strLen.apply("Hello")); // 5
// Consumer: 打印每個元素
Consumer printer = System.out::println;
printer.accept("Consumer 輸出");
// Supplier: 惰性生成隨機數
Supplier random = Math::random;
System.out.println(random.get());
// 組合使用: 過濾 -> 轉換 -> 消費
List words = Arrays.asList("a", "ab", "abc", "abcd", "abcde");
words.stream()
.filter(longEnough) // Predicate
.map(strLen) // Function
.forEach(printer); // Consumer
}
}
Lambda 只能引用最終語義最終的局部變量 (不可變),即編譯期可推導為 final:
int factor = 2; // factor = 3; // 取消註釋會編譯錯誤 Functionmultiplier = x -> x * factor; // OK,factor 未改變 // 需可變狀態?用最終數組/Atomic 包裝 final int[] counter = {0}; Runnable r = () -> counter[0]++; // OK // 或 AtomicInteger atomic = new AtomicInteger(); Runnable r2 = () -> atomic.incrementAndGet();
| 類型 | 語法 | 對應 Lambda | 示例 |
|---|---|---|---|
| 靜態方法 | Class::staticMethod | (args) -> Class.staticMethod(args) | Integer::parseInt |
| 實例方法 (特定對象) | obj::instanceMethod | (args) -> obj.instanceMethod(args) | System.out::println |
| 實例方法 (任意對象) | Class::instanceMethod | (obj, args) -> obj.instanceMethod(args) | String::length |
| 構造方法 | Class::new | (args) -> new Class(args) | ArrayList::new |
// app/build.gradle (舊版 Jack 工具鏈,已棄用)
android {
defaultConfig {
jackOptions { enabled true }
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
// 現代做法 (AGP 4.0+): 無需額外配置,直接用 Java 8 語法
// 確保 compileSdk 29+、minSdk 24+ (API 24 以下需啟用 coreLibraryDesugaring)
android {
compileOptions {
coreLibraryDesugaringEnabled true
}
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
}
(params) -> body,類型推導、單語句省略 {}/return。@FunctionalInterface 校驗。:: 讓代碼更聲明式、更簡潔。Serializable 的函數式接口才可序列化)。