源本科技 | 码上会

Java 方法引用

2026/01/29
35
0

学习目标

  • 理解 Java 方法引用的基本概念与使用场景

  • 掌握四种方法引用的语法与适用条件

  • 能够将 Lambda 表达式简化为方法引用

  • 熟悉方法引用与函数式接口的配合方式


什么是方法引用

Java 方法引用是 Java 8 引入的一种语法特性,用于以更简洁的方式引用已存在的方法,而无需实际调用它。它是 Lambda 表达式的进一步简化形式,旨在提升代码的可读性并减少样板代码。方法引用使用双冒号 :: 操作符,并且只能用于函数式接口(即仅包含一个抽象方法的接口)。

关键前提:只有当 Lambda 表达式体中仅调用一个已有方法时,才能使用方法引用进行替换。


方法引用的四种类型

1. 静态方法引用

引用类的静态方法,使用 类名::静态方法名 的形式。

import java.util.*;

class MathUtil {
    static void square(int n) {
        System.out.println(n * n);
    }
}

class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(2, 3, 4);
        list.forEach(MathUtil::square);
    }
}

输出

4
9
16

等价于 Lambda 表达式:

list.forEach(n -> MathUtil.square(n));

适用于工具类中的静态方法(如 Math.abs, Collections.sort 等)


2. 特定对象的实例方法引用

引用某个已创建对象的实例方法,使用 对象引用::实例方法名

import java.util.*;

class Printer {
    void print(String msg) {
        System.out.println(msg);
    }
}

class Main {
    public static void main(String[] args) {
        Printer printer = new Printer();
        List<String> data = Arrays.asList("Java", "Spring", "Boot");
        data.forEach(printer::print);
    }
}

输出

Java
Spring
Boot

等价于:

data.forEach(msg -> printer.print(msg));

适用于需要复用某个特定对象行为的场景


3. 任意对象的实例方法引用

引用某类任意实例的实例方法,使用 类名::实例方法名。运行时,该方法会作用于流或集合中的每个元素。

import java.util.*;

class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("java", "spring", "microservice");
        
        names.stream()
             .map(String::toUpperCase)
             .forEach(System.out::println);
    }
}

输出

JAVA
SPRING
MICROSERVICE

等价于:

names.stream()
     .map(name -> name.toUpperCase())
     .forEach(s -> System.out.println(s));

广泛用于 Stream API 中对元素进行转换、比较等操作(如 String::length, Integer::compareTo


4. 构造器引用

引用类的构造方法,使用 类名::new,常用于通过函数式接口创建对象。

import java.util.function.Supplier;

class Student {
    Student() {
        System.out.println("Student object created");
    }
}

class Main {
    public static void main(String[] args) {
        Supplier<Student> supplier = Student::new;
        supplier.get(); // 触发构造
    }
}

输出

Student object created

等价于:

Supplier<Student> supplier = () -> new Student();

常与 Supplier<T>Function<T,R> 等函数式接口配合,实现工厂模式或延迟初始化


与函数式接口的关系

方法引用必须与函数式接口配合使用。其核心要求是:

  • 函数式接口的抽象方法签名(参数类型、返回值)必须与被引用方法兼容

  • 常见的函数式接口包括:

    • Consumer<T>:接收一个参数,无返回(如 System.out::println

    • Supplier<T>:无参,有返回(如 LocalDateTime::now

    • Function<T, R>:接收一个参数,返回一个结果(如 String::toLowerCase

    • Predicate<T>:接收一个参数,返回布尔值(如 String::isEmpty

应用示例

List<String> fruits = Arrays.asList("Banana", "Apple", "Mango");

// 使用方法引用进行忽略大小写的排序
fruits.sort(String::compareToIgnoreCase);

此处 String::compareToIgnoreCase 是一个 (String, String) -> int 类型的方法,恰好匹配 Comparator<String> 接口的 compare 方法签名。


重点总结

  • 方法引用是 Lambda 表达式的语法糖,用于简化对已有方法的调用

  • 共有四种类型:静态方法、特定对象实例方法、任意对象实例方法、构造器

  • 必须与函数式接口配合使用,且方法签名需匹配

  • 能显著提升代码简洁性和可读性,尤其在 Stream 和集合操作中

  • 不能用于调用多个方法或包含复杂逻辑的场景


思考题

  1. 为什么 Arrays.asList("a", "b").stream().map(String::new) 这段代码无法编译?请分析原因。

  2. 在什么情况下,你更倾向于使用 Lambda 表达式而不是方法引用?举例说明。

  3. 尝试将以下 Lambda 表达式改写为方法引用:

    list.sort((s1, s2) -> s1.compareTo(s2));