Method References in Java – Java中的方法引用

最后修改: 2019年 2月 5日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

One of the most welcome changes in Java 8 was the introduction of lambda expressions, as these allow us to forego anonymous classes, greatly reducing boilerplate code and improving readability.

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

Method references are a special type of lambda expressions. They’re often used to create simple lambda expressions by referencing existing methods.

方法引用是lambda表达式的一种特殊类型。它们经常被用来通过引用现有的方法来创建简单的lambda表达式。

There are four kinds of method references:

有四种方法参考。

  • Static methods
  • Instance methods of particular objects
  • Instance methods of an arbitrary object of a particular type
  • Constructor

In this tutorial, we’ll explore method references in Java.

在本教程中,我们将探讨Java中的方法引用。

2. Reference to a Static Method

2.对一个静态方法的引用

We’ll begin with a very simple example, capitalizing and printing a list of Strings:

我们将从一个非常简单的例子开始,大写并打印一个字符串的列表。

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

We can achieve this by leveraging a simple lambda expression calling the StringUtils.capitalize() method directly:

我们可以通过利用一个简单的lambda表达式直接调用StringUtils.capitalize()方法来实现这一点。

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

Or, we can use a method reference to simply refer to the capitalize static method:

或者,我们可以使用一个方法引用来简单地引用capitalize静态方法。

messages.forEach(StringUtils::capitalize);

Notice that method references always utilize the :: operator.

注意,方法引用总是利用::操作符。

3. Reference to an Instance Method of a Particular Object

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

To demonstrate this type of method reference, let’s consider two classes:

为了证明这种类型的方法引用,让我们考虑两个类。

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());
    }

}

And, let’s create a BicycleComparator object to compare bicycle frame sizes:

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

BicycleComparator bikeFrameSizeComparator = new BicycleComparator();

We could use a lambda expression to sort bicycles by frame size, but we’d need to specify two bikes for comparison:

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

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

Instead, we can use a method reference to have the compiler handle parameter passing for us:

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

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

The method reference is much cleaner and more readable, as our intention is clearly shown by the code.

方法的引用要干净得多,也更有可读性,因为我们的意图被代码清楚地显示出来了。

4. Reference to an Instance Method of an Arbitrary Object of a Particular Type

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

This type of method reference is similar to the previous example, but without having to create a custom object to perform the comparison.

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

Let’s create an Integer list that we want to sort:

让我们创建一个我们想要排序的Integer列表。

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

If we use a classic lambda expression, both parameters need to be explicitly passed, while using a method reference is much more straightforward:

如果我们使用一个经典的lambda表达式,两个参数都需要明确地传递,而使用方法引用则要直接得多。

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

Even though it’s still a one-liner, the method reference is much easier to read and understand.

尽管它仍然是一个单行本,但方法参考更容易阅读和理解。

5. Reference to a Constructor

5.对构造函数的引用

We can reference a constructor in the same way that we referenced a static method in our first example. The only difference is that we’ll use the new keyword.

我们可以像在第一个例子中引用静态方法一样来引用构造函数。唯一的区别是,我们将使用new关键字。

Let’s create a Bicycle array out of a String list with different brands:

让我们用不同品牌的String列表来创建一个Bicycle数组。

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

First, we’ll add a new constructor to our Bicycle class:

首先,我们要给我们的Bicycle类添加一个新的构造函数。

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

Next, we’ll use our new constructor from a method reference and make a Bicycle array from the original String list:

接下来,我们将从方法引用中使用我们的新构造函数,并从原始的String列表中制作一个Bicycle阵列。

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

Notice how we called both Bicycle and Array constructors using a method reference, giving our code a much more concise and clear appearance.

注意我们是如何使用方法引用来调用BicycleArray构造函数的,使我们的代码看起来更加简洁明了。

6. Additional Examples and Limitations

6.其他例子和限制

As we’ve seen so far, method references are a great way to make our code and intentions very clear and readable. However, we can’t use them to replace all kinds of lambda expressions since they have some limitations.

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

Their main limitation is a result of what’s also their biggest strength: the output from the previous expression needs to match the input parameters of the referenced method signature.

它们的主要限制是由它们最大的优势造成的。前面表达式的输出需要与被引用方法签名的输入参数相匹配

Let’s see an example of this limitation:

让我们看看这种限制的例子。

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

This simple case can’t be expressed with a method reference, because the printf method requires 3 parameters in our case, and using createBicyclesList().forEach() would only allow the method reference to infer one parameter (the Bicycle object).

这个简单的案例不能用方法引用来表达,因为在我们的案例中,printf方法需要3个参数,而使用createBicyclesList().forEach()就只能让方法引用推断出一个参数(Bicycle对象)。

Finally, let’s explore how to create a no-operation function that can be referenced from a lambda expression.

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

In this case, we’ll want to use a lambda expression without using its parameters.

在这种情况下,我们要使用一个lambda表达式,而不使用它的参数。

First, let’s create the doNothingAtAll method:

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

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

As it is a varargs method, it will work in any lambda expression, no matter the referenced object or number of parameters inferred.

由于它是一个varargs方法,它将在任何lambda表达式中工作,不管引用的对象或推断的参数数量。

Now, let’s see it in action:

现在,让我们看看它的行动。

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

7. Conclusion

7.结论

In this quick tutorial, we learned what method references are in Java and how to use them to replace lambda expressions, thereby improving readability and clarifying the programmer’s intent.

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

All code presented in this article is available over on GitHub.

本文介绍的所有代码都可以在GitHub上获得