方法引用是 Lambda 表達式的「語法糖升級版」,當 Lambda 體只調用一個現有方法時,方法引用提供更簡潔、可讀性更強的寫法。本文基於 JDK 8 官方規範,系統講解方法引用的四大分類、語法結構、類型推導、泛型支持、構造器引用、數組構造器引用,並通過大量代碼示例演示等價 Lambda 轉換與實戰應用模式。
方法引用:使用
::操作符引用現有方法或構造器,不執行方法,而是生成一個函數式接口實例。
唯一用途:支持 Lambda 簡寫,提升代碼可讀性與聲明式表達能力。
核心語法:ClassName::methodName、instance::methodName、ClassName::new、Type[]::new
| 方法引用 | 等價 Lambda | 函數式接口示例 |
|---|---|---|
String::valueOf | s -> String.valueOf(s) | Function<Object, String> |
Math::pow | (x, y) -> Math.pow(x, y) | DoubleBinaryOperator |
System.out::println | s -> System.out.println(s) | Consumer<String> |
"abc"::startsWith | s -> "abc".startsWith(s) | Predicate<String> |
String::compareToIgnoreCase | (s1, s2) -> s1.compareToIgnoreCase(s2) | Comparator<String> |
ArrayList::new | () -> new ArrayList() | Supplier<List> |
int[]::new | size -> new int[size] | IntFunction<int[]> |

語法:ClassName::staticMethodName
適用場景:工具類方法、純函數、無狀態操作
// 1. 基礎用法 Functionparser = Integer::parseInt; Integer result = parser.apply("123"); // 123 // 2. 多參數靜態方法 BinaryOperator power = Math::pow; Double result = power.apply(2.0, 3.0); // 8.0 // 3. 集合工具類方法引用 (需匹配函數式接口) List numbers = Arrays.asList(1, 10, 3, 5); Function , Integer> maxFn = Collections::max; Integer max = maxFn.apply(numbers); // 10 // 等價 Lambda Function
, Integer> maxFnLambda = list -> Collections.max(list);
@FunctionalInterface
interface StringFunc {
String func(String n);
}
class MyStringOps {
// 靜態方法
public static String strReverse(String str) {
StringBuilder sb = new StringBuilder(str);
return sb.reverse().toString();
}
}
class MethodRefDemo {
public static String stringOp(StringFunc sf, String s) {
return sf.func(s);
}
public static void main(String[] args) {
String inStr = "lambda add power to Java";
// MyStringOps::strReverse 實現了 StringFunc.func()
String outStr = stringOp(MyStringOps::strReverse, inStr);
System.out.println("Original: " + inStr);
System.out.println("Reversed: " + outStr);
}
}
// 輸出: Original: lambda add power to Java
// Reversed: avaJ ot rewop dda adbmal
class MyClass {
private int val;
MyClass(int v) { val = v; }
public int getValue() { return val; }
}
class UseMethodRef {
// 靜態比較方法
public static int compareMC(MyClass a, MyClass b) {
return a.getValue() - b.getValue();
}
public static void main(String[] args) {
List list = Arrays.asList(
new MyClass(1), new MyClass(4), new MyClass(2),
new MyClass(9), new MyClass(3), new MyClass(7)
);
// Collections.max 需要 Comparator
// UseMethodRef::compareMC 自動適配為 Comparator
MyClass max = Collections.max(list, UseMethodRef::compareMC);
System.out.println("Max value: " + max.getValue()); // 9
}
}
語法:instanceReference::methodName
class MyStringOps {
// 實例方法
public String strReverse(String str) {
return new StringBuilder(str).reverse().toString();
}
}
public class Demo {
public static void main(String[] args) {
MyStringOps ops = new MyStringOps(); // 具體實例
StringFunc sf = ops::strReverse; // 引用該實例的方法
System.out.println(sf.func("Hello")); // olleH
}
}
語法:ClassName::instanceMethodName
關鍵規則:函數式接口第一個參數成為方法調用者 (this),後續參數映射為方法參數。
@FunctionalInterface interface MyFunc{ boolean func(T v1, T v2); } class HighTemp { private int hTemp; HighTemp(int ht) { hTemp = ht; } // 實例方法:比較溫度 public boolean sameTemp(HighTemp ht2) { return hTemp == ht2.hTemp; } public boolean lessThanTemp(HighTemp ht2) { return hTemp < ht2.hTemp; } } public class InstanceMethDemo { // 計數器:統計滿足條件的元素個數 public static int counter(T[] vals, MyFunc f, T v) { int count = 0; for (T val : vals) if (f.func(val, v)) count++; return count; } public static void main(String[] args) { HighTemp[] week = { new HighTemp(89), new HighTemp(82), new HighTemp(90), new HighTemp(89), new HighTemp(89), new HighTemp(91), new HighTemp(84), new HighTemp(83) }; // HighTemp::sameTemp → (v1, v2) -> v1.sameTemp(v2) int count = counter(week, HighTemp::sameTemp, new HighTemp(89)); System.out.println(count + " days had high of 89"); // 3 count = counter(week, HighTemp::lessThanTemp, new HighTemp(89)); System.out.println(count + " days less than 89"); // 3 } }
原理解析:HighTemp::sameTemp 中,sameTemp 簽名為 boolean sameTemp(HighTemp),但函數式接口要求 boolean func(HighTemp, HighTemp)。編譯器自動將第一個參數作為調用者 (this),第二個參數作為方法參數,完美匹配。
語法:super::methodName
class Child extends Parent {
@Override
public void greet() {
super::greet; // 引用父類實現
// 等價: () -> super.greet()
}
}
語法:ClassName::new
本質:構造器是特殊的靜態方法,方法名為 new。
@FunctionalInterface
interface MyFunc {
MyClass func(int n);
}
class MyClass {
private int val;
MyClass(int v) { val = v; }
MyClass() { val = 0; }
public int getValue() { return val; }
}
public class ConstructorRefDemo {
public static void main(String[] args) {
// 引用帶 int 參數的構造器
MyFunc myClassCons = MyClass::new;
MyClass mc = myClassCons.func(100);
System.out.println("val: " + mc.getValue()); // 100
// 無參構造器
Supplier supplier = MyClass::new;
MyClass mc2 = supplier.get();
System.out.println("default val: " + mc2.getValue()); // 0
}
}
Listnames = Arrays.asList("Alice", "Bob", "Charlie"); // 錯誤寫法:Button 非函數式接口 // List
語法:TypeName[]::new
對應函數式接口:IntFunction<Type[]> (單 int 參數 → 數組)
// 1. 基礎用法 IntFunctionarrayMaker = int[]::new; int[] arr = arrayMaker.apply(10); // new int[10] // 2. Stream toArray 經典應用 String[] strArray = names.stream() .toArray(String[]::new); // 等價 size -> new String[size] // 3. 多維數組 (需嵌套) IntFunction matrixMaker = size -> new int[size][]; int[][] matrix = matrixMaker.apply(3); // new int[3][]
class MyArrayOps {
// 泛型靜態方法
public static int countMatching(T[] vals, T v) {
int count = 0;
for (T val : vals) if (val == v || (val != null && val.equals(v))) count++;
return count;
}
}
@FunctionalInterface
interface MyFunc {
int func(T[] vals, T v);
}
public class GenericMethodRefDemo {
public static int myOp(MyFunc f, T[] vals, T v) {
return f.func(vals, v);
}
public static void main(String[] args) {
Integer[] nums = {1, 2, 3, 4, 2, 3, 4, 4, 5};
String[] strs = {"One", "Two", "Three", "Two"};
// 類型推導自動確定 T 為 Integer
int count = myOp(MyArrayOps::countMatching, nums, 4);
System.out.println("nums contains " + count + " 4s"); // 3
// 類型推導自動確定 T 為 String
count = myOp(MyArrayOps::countMatching, strs, "Two");
System.out.println("strs contains " + count + " Twos"); // 2
// 顯式指定類型參數 (罕見需要)
count = myOp(MyArrayOps::countMatching, nums, 4);
}
}
類型參數位置規則:
ClassName::methodName (:: 後、方法名前)ClassName::new | 形式 | 等價 Lambda | 說明 |
|---|---|---|
this::method | args -> this.method(args) | 引用當前對象實例方法 |
super::method | args -> super.method(args) | 引用父類實例方法 |
ClassName.this::method | args -> ClassName.this.method(args) | 內部類引用外部類實例方法 |
ClassName.super::method | args -> ClassName.super.method(args) | 內部類引用外部類父類方法 |
| 場景 | 推薦 | 理由 |
|---|---|---|
| 調用現有靜態方法 | 方法引用 | 語義清晰、無參數聲明噪音 |
| 調用特定對象實例方法 | 方法引用 | 捕獲 this 引用明確 |
| 調用任意對象實例方法 (流式 API) | 方法引用 | list.stream().map(String::toUpperCase) 極具可讀性 |
| 構造對象 / 數組 | 構造器引用 | ArrayList::new、String[]::new 標準寫法 |
| 需要參數轉換、多語句、條件邏輯 | Lambda | 方法引用無法表達複雜邏輯 |
| 調用方法名與函數式接口方法名不同 | Lambda | 方法引用要求簽名兼容 |
| 需要顯式類型參數 (泛型模糊) | Lambda | Lambda 參數可顯式聲明類型 |
Listusers = userService.findAll(); // 方法引用鏈:提取名稱 → 去重 → 排序 → 收集 List names = users.stream() .map(User::getName) // 實例方法引用 .filter(StringUtils::isNotBlank) // 靜態方法引用 .distinct() .sorted(String::compareToIgnoreCase) // 類型上實例方法引用 .collect(Collectors.toList());
Comparatorcomparator = Comparator .comparing(User::getDept) // 主鍵:部門 .thenComparing(User::getJoinDate) // 次鍵:入職時間 .thenComparing(User::getName); // 三鍵:姓名 users.sort(comparator);
// 錯誤:方法引用無法拋 IOException
// Files.lines(path).forEach(System.out::println);
// 正確:Lambda 包裝
Files.lines(path).forEach(line -> {
try { System.out.println(line); }
catch (Exception e) { log.error(e); }
});
// 或使用工具方法
Files.lines(path).forEach(ExceptionUtils.wrapConsumer(System.out::println));
// 根據類型提取不同屬性 Function
方法引用以 :: 為核心語法,將「引用現有代碼」提升為一等公民,實現了從「如何做」到「做什麼」的聲明式轉變。掌握 靜態方法、實例方法 (特定/任意/超類)、構造器、數組構造器 四大類型,理解 函數式接口目標類型推導、泛型類型參數位置、this/super 捕獲語義,能讓 Stream API、Comparator、工廠模式、註冊回調等代碼更簡潔、更具表達力。記住:方法引用是 Lambda 的子集,當 Lambda 體僅委託單一方法時,優先考慮方法引用;否則堅持使用 Lambda。