1. 概述

Java 8 中最令人期待的特性之一就是引入了 Lambda 表达式,它让我们可以告别匿名类,极大地减少了样板代码,提升了代码可读性。

方法引用(Method References)则是 Lambda 表达式的一种特殊形式。我们可以通过方法引用来创建简洁的 Lambda 表达式,直接引用已有的方法。

方法引用主要分为以下四类:

  • 静态方法引用
  • 特定对象的实例方法引用
  • 某个类型任意对象的实例方法引用
  • 构造器引用

本文将带你深入理解 Java 中的方法引用。

2. 静态方法引用

我们从一个简单的例子开始:将一组字符串首字母大写并输出。

List<String> messages = Arrays.asList("hello", "baeldung", "readers!");

我们可以通过 Lambda 表达式调用 StringUtils.capitalize() 方法来实现:

messages.forEach(word -> StringUtils.capitalize(word));

或者,使用方法引用,直接引用 capitalize 静态方法:

messages.forEach(StringUtils::capitalize);

注意:方法引用统一使用 :: 操作符。

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

我们来看一个实际的例子,定义两个类:

public class Bicycle {

    private String brand;
    private Integer frameSize;
    // 标准构造函数、getter 和 setter
}

public class BicycleComparator implements Comparator<Bicycle> {

    @Override
    public int compare(Bicycle a, Bicycle b) {
        return a.getFrameSize().compareTo(b.getFrameSize());
    }

}

接着创建一个比较器对象:

BicycleComparator bikeFrameSizeComparator = new BicycleComparator();

如果我们使用 Lambda 表达式来排序自行车的车架尺寸,需要手动传入两个对象:

createBicyclesList().stream()
  .sorted((a, b) -> bikeFrameSizeComparator.compare(a, b));

而使用方法引用则更简洁,参数传递由编译器自动处理:

createBicyclesList().stream()
  .sorted(bikeFrameSizeComparator::compare);

✅ 方法引用不仅更简洁,还更直观地表达了我们的意图。

4. 某个类型任意对象的实例方法引用

这种引用方式与上一种类似,但不需要我们手动创建比较器对象。

比如我们有一个 Integer 列表:

List<Integer> numbers = Arrays.asList(5, 3, 50, 24, 40, 2, 9, 18);

使用 Lambda 表达式时,我们需要手动传参:

numbers.stream()
  .sorted((a, b) -> a.compareTo(b));

而使用方法引用则更简洁明了:

numbers.stream()
  .sorted(Integer::compareTo);

✅ 虽然都是一行代码,但方法引用更易读、更优雅。

5. 构造器引用

构造器引用的语法和静态方法引用类似,唯一的区别是使用 new 关键字。

假设我们有一个品牌列表,想要创建对应的 Bicycle 对象数组:

List<String> bikeBrands = Arrays.asList("Giant", "Scott", "Trek", "GT");

首先,为 Bicycle 类添加一个构造器:

public Bicycle(String brand) {
    this.brand = brand;
    this.frameSize = 0;
}

然后使用构造器引用:

bikeBrands.stream()
  .map(Bicycle::new)
  .toArray(Bicycle[]::new);

✅ 注意这里我们同时使用了 Bicycle::newBicycle[]::new,代码非常简洁清晰。

6. 更多示例与限制

方法引用虽然强大,但不能替代所有 Lambda 表达式,因为它有一些限制。

⚠️ 核心限制:方法引用要求前一个表达式的输出必须与被引用方法的参数类型匹配。

举个例子:

createBicyclesList().forEach(b -> System.out.printf(
  "Bike brand is '%s' and frame size is '%d'%n",
  b.getBrand(),
  b.getFrameSize()));

这段代码不能用方法引用代替,因为 printf 需要三个参数,而 forEach 只能提供一个(即 Bicycle 对象)。

最后我们来看一个“无操作”函数的示例:

有时我们想使用 Lambda 表达式但不想使用它的参数。

定义一个通用的无操作方法:

private static <T> void doNothingAtAll(Object... o) {
}

这是一个 可变参数 方法,可以适配任何 Lambda 表达式。

使用方式如下:

createBicyclesList()
  .forEach((o) -> MethodReferenceExamples.doNothingAtAll(o));

7. 总结

在这篇文章中,我们介绍了 Java 中的方法引用,以及如何用它来替代 Lambda 表达式,从而提升代码的可读性和表达力。

虽然方法引用不能覆盖所有 Lambda 场景,但在合适的地方使用,能让你的代码更简洁、更专业。✅


原始标题:Method References in Java | Baeldung