Java 方法參考 (Method Reference)
方法參考是 Lambda 表達式的簡化寫法,當 Lambda 只是呼叫一個已存在的方法時,可以用 :: 運算子來參考該方法。
四種方法參考類型
1. 靜態方法參考
語法:類別名稱::靜態方法名稱
// Lambda
Function<String, Integer> parser1 = s -> Integer.parseInt(s);
// 方法參考
Function<String, Integer> parser2 = Integer::parseInt;
System.out.println(parser2.apply("123")); // 123
// 排序範例
List<Integer> nums = Arrays.asList(3, 1, 4, 1, 5);
nums.sort((a, b) -> Integer.compare(a, b)); // Lambda
nums.sort(Integer::compare); // 方法參考
2. 實例方法參考(特定物件)
語法:物件::實例方法名稱
String prefix = "Hello, ";
// Lambda
Function<String, String> greeter1 = s -> prefix.concat(s);
// 方法參考
Function<String, String> greeter2 = prefix::concat;
System.out.println(greeter2.apply("World")); // Hello, World
// 另一個範例
PrintStream out = System.out;
Consumer<String> printer = out::println;
printer.accept("Hello"); // Hello
3. 實例方法參考(任意物件)
語法:類別名稱::實例方法名稱
第一個參數會成為方法呼叫的對象。
// Lambda
Function<String, String> upper1 = s -> s.toUpperCase();
// 方法參考
Function<String, String> upper2 = String::toUpperCase;
System.out.println(upper2.apply("hello")); // HELLO
// 兩個參數的情況
BiFunction<String, String, Boolean> startsWith1 = (s, prefix) -> s.startsWith(prefix);
BiFunction<String, String, Boolean> startsWith2 = String::startsWith;
System.out.println(startsWith2.apply("Hello", "He")); // true
// 排序範例
List<String> names = Arrays.asList("Bob", "Alice", "Charlie");
names.sort((a, b) -> a.compareToIgnoreCase(b)); // Lambda
names.sort(String::compareToIgnoreCase); // 方法參考
4. 建構子參考
語法:類別名稱::new
// Lambda
Supplier<List<String>> listFactory1 = () -> new ArrayList<>();
// 方法參考
Supplier<List<String>> listFactory2 = ArrayList::new;
List<String> list = listFactory2.get();
// 帶參數的建構子
Function<String, StringBuilder> sbFactory = StringBuilder::new;
StringBuilder sb = sbFactory.apply("Hello");
// 陣列建構子
IntFunction<int[]> arrayFactory = int[]::new;
int[] arr = arrayFactory.apply(10); // 建立長度為 10 的陣列
實際應用
Stream 操作
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 轉換
List<Integer> lengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// 過濾
List<String> nonEmpty = names.stream()
.filter(s -> !s.isEmpty()) // 這個無法用方法參考簡化
.collect(Collectors.toList());
// 輸出
names.forEach(System.out::println);
// 收集到新集合
Set<String> nameSet = names.stream()
.collect(Collectors.toCollection(HashSet::new));
物件建立
record Person(String name) {}
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 使用建構子參考建立物件
List<Person> people = names.stream()
.map(Person::new)
.collect(Collectors.toList());
Optional
Optional<String> optional = Optional.of("Hello");
optional.ifPresent(System.out::println);
optional.map(String::toUpperCase).ifPresent(System.out::println);
Lambda vs 方法參考
// Lambda
list.forEach(s -> System.out.println(s));
list.sort((a, b) -> a.compareTo(b));
list.stream().map(s -> s.toUpperCase());
// 方法參考(更簡潔)
list.forEach(System.out::println);
list.sort(String::compareTo);
list.stream().map(String::toUpperCase);
何時使用方法參考
✅ 適合使用:
- Lambda 只是呼叫一個方法
- 不需要對參數做額外處理
- 程式碼更清晰易讀
❌ 不適合使用:
- 需要對參數做處理
- 需要多個操作
- 方法參考反而降低可讀性
// 不適合用方法參考的情況
list.stream()
.filter(s -> s.length() > 5) // 需要比較操作
.map(s -> s.substring(0, 3)) // 需要參數
.map(s -> "prefix_" + s) // 需要字串串接
.forEach(System.out::println); // 這個可以用
重點整理
| 類型 | 語法 | Lambda 等價 |
|---|---|---|
| 靜態方法 | Class::staticMethod | x -> Class.staticMethod(x) |
| 實例方法(特定物件) | obj::instanceMethod | x -> obj.instanceMethod(x) |
| 實例方法(任意物件) | Class::instanceMethod | x -> x.instanceMethod() |
| 建構子 | Class::new | x -> new Class(x) |
- 方法參考是 Lambda 的簡化形式
- 使用
::運算子 - 增加程式碼可讀性
- 不是所有 Lambda 都能轉為方法參考