Java 中的方法引用

评论 0 浏览 0 2019-02-06

1. 概述

Java 8 中最受欢迎的变化之一是引入了 lambda 表达式,因为它们允许我们放弃匿名类,大大减少了样板代码并提高了可读性。

方法引用是一种特殊类型的 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;
    // standard constructor, getters and setters
}

public class BicycleComparator implements Comparator {

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

}

并且,让我们创建一个 BicycleComparator 对象来比较自行车车架尺寸:

BicycleComparator bikeFrameSizeComparator = new BicycleComparator();

我们可以使用 lambda 表达式按车架尺寸对自行车进行排序,但我们需要指定两辆自行车进行比较:

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

相反,我们可以使用方法引用让编译器为我们处理参数传递:

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

方法引用更加清晰且更具可读性,因为代码清楚地表明了我们的意图。

4. 引用特定类型的任意对象的实例方法

这种类型的方法引用与前面的示例类似,但无需创建自定义对象来执行比较。

让我们创建一个要排序的整数列表:

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 关键字。

让我们从具有不同品牌的 String 列表中创建一个 Bicycle 数组:

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

首先,我们将向 Bicycle 类添加一个新的构造函数:

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

接下来,我们将使用方法引用中的新构造函数,并从原始 String 列表中创建一个 Bicycle 数组:

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

请注意我们如何使用方法引用调用 BicycleArray 构造函数,使我们的代码外观更加简洁和清晰。

6. 其他示例和限制

正如我们到目前为止所看到的,方法引用是使我们的代码和意图变得非常清晰和可读的好方法。但是,我们不能用它们来替换所有类型的 lambda 表达式,因为它们有一些限制。

它们的主要限制是其最大的优势:先前表达式的输出需要与引用的方法签名的输入参数相匹配

让我们看一下此限制的示例:

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

这个简单的例子无法用方法引用来表达,因为在我们的例子中,printf方法需要3个参数,而使用createBicyclesList().forEach()只会允许用于推断一个参数(Bicycle 对象)的方法引用。

最后,我们来探讨一下如何创建一个可以从 lambda 表达式引用的无操作函数。

在这种情况下,我们希望使用 lambda 表达式而不使用其参数。

首先,让我们创建 doNothingAtAll 方法:

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

由于它是一个 varargs 方法,因此它可以在任何 lambda 表达式中工作,无论引用的对象或推断的参数数量如何。

现在,让我们看看它的实际效果:

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

七、结论

在这个快速教程中,我们了解了 Java 中的方法引用是什么以及如何使用它们来替换 lambda 表达式,从而提高可读性并阐明程序员的意图。

本文中提供的所有代码均可在 GitHub 上 获取。

最后更新2023-11-10
0 个评论
标签