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::staticMethodx -> Class.staticMethod(x)
實例方法(特定物件)obj::instanceMethodx -> obj.instanceMethod(x)
實例方法(任意物件)Class::instanceMethodx -> x.instanceMethod()
建構子Class::newx -> new Class(x)
  • 方法參考是 Lambda 的簡化形式
  • 使用 :: 運算子
  • 增加程式碼可讀性
  • 不是所有 Lambda 都能轉為方法參考