Java Lambda 表達式
Lambda 表達式是 Java 8 引入的功能,提供簡潔的方式來實作函數式介面。
基本語法
(參數) -> 表達式
(參數) -> { 程式碼區塊 }
從匿名類別到 Lambda
// 匿名類別
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};
// Lambda
Runnable r2 = () -> System.out.println("Hello");
各種形式
// 無參數
Runnable r = () -> System.out.println("Hello");
// 一個參數(可省略括號)
Consumer<String> c = s -> System.out.println(s);
// 多個參數
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
// 帶有型別
BiFunction<Integer, Integer, Integer> add2 = (Integer a, Integer b) -> a + b;
// 多行程式碼
BiFunction<Integer, Integer, Integer> divide = (a, b) -> {
if (b == 0) {
throw new ArithmeticException("除數不能為 0");
}
return a / b;
};
函數式介面
Lambda 只能用於函數式介面(只有一個抽象方法):
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
}
// 使用 Lambda
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
System.out.println(add.calculate(5, 3)); // 8
System.out.println(multiply.calculate(5, 3)); // 15
更多說明請參考 Java 函數式介面。
常用內建函數式介面
| 介面 | 說明 | 方法 |
|---|---|---|
Predicate<T> | 判斷 | boolean test(T t) |
Function<T, R> | 轉換 | R apply(T t) |
Consumer<T> | 消費 | void accept(T t) |
Supplier<T> | 供應 | T get() |
BiFunction<T, U, R> | 雙參數轉換 | R apply(T t, U u) |
// Predicate:判斷
Predicate<Integer> isPositive = n -> n > 0;
System.out.println(isPositive.test(5)); // true
// Function:轉換
Function<String, Integer> length = s -> s.length();
System.out.println(length.apply("Hello")); // 5
// Consumer:消費
Consumer<String> print = s -> System.out.println(s);
print.accept("Hello");
// Supplier:供應
Supplier<Double> random = () -> Math.random();
System.out.println(random.get());
更多說明請參考 Java 內建函數式介面。
方法參考
更簡潔的語法:
// Lambda
Consumer<String> c1 = s -> System.out.println(s);
// 方法參考
Consumer<String> c2 = System.out::println;
四種方法參考
// 1. 靜態方法參考
Function<String, Integer> parse = Integer::parseInt;
// 2. 實例方法參考(特定物件)
String str = "Hello";
Supplier<Integer> len = str::length;
// 3. 實例方法參考(任意物件)
Function<String, Integer> length = String::length;
// 4. 建構子參考
Supplier<ArrayList<String>> supplier = ArrayList::new;
更多說明請參考 Java 方法參考。
實際應用
集合操作
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 遍歷
names.forEach(name -> System.out.println(name));
names.forEach(System.out::println);
// 排序
names.sort((a, b) -> a.length() - b.length());
// 條件移除
names.removeIf(s -> s.startsWith("A"));
Stream
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 過濾偶數,乘以 2,求總和
int sum = numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * 2)
.reduce(0, Integer::sum);
// 60
排序
List<Person> people = getPeople();
// 依年齡排序
people.sort((p1, p2) -> p1.getAge() - p2.getAge());
// 或使用 Comparator
people.sort(Comparator.comparing(Person::getAge));
people.sort(Comparator.comparing(Person::getName).reversed());
執行緒
// Runnable
new Thread(() -> {
System.out.println("執行緒執行中");
}).start();
// Callable
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
return 42;
});
變數捕獲
Lambda 可以存取外部變數,但必須是 effectively final:
int multiplier = 10; // effectively final
Function<Integer, Integer> multiply = n -> n * multiplier;
// multiplier = 20; // 錯誤!不能修改
Lambda vs 匿名類別
| 特性 | Lambda | 匿名類別 |
|---|---|---|
| 語法 | 簡潔 | 冗長 |
| this | 外部類別 | 匿名類別本身 |
| 適用範圍 | 函數式介面 | 任何介面/類別 |
| 效能 | 較好 | 產生額外類別檔 |