# 方法引用

> 原文： [https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html](https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html)

您使用 [lambda 表达式](lambdaexpressions.html)来创建匿名方法。但是，有时，lambda 表达式只会调用现有方法。在这些情况下，通过名称引用现有方法通常更清楚。方法引用使您可以这样做;对于已有名称的方法，它们是紧凑的，易于阅读的 lambda 表达式。

再次考虑 [Lambda 表达式](lambdaexpressions.html)一节中讨论的 [`Person`](examples/Person.java) 类：

```
public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public Calendar getBirthday() {
        return birthday;
    }    

    public static int compareByAge(Person a, Person b) {
        return a.birthday.compareTo(b.birthday);
    }}
```

假设您的社交网络应用程序的成员包含在一个数组中，并且您希望按年龄对数组进行排序。您可以使用以下代码（在示例 [`MethodReferencesTest`](examples/MethodReferencesTest.java) 中找到本节中描述的代码摘录）：

```
Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);

class PersonAgeComparator implements Comparator<Person> {
    public int compare(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}

Arrays.sort(rosterAsArray, new PersonAgeComparator());
```

`sort`调用的方法签名如下：

```
static <T> void sort(T[] a, Comparator<? super T> c)
```

请注意，接口`Comparator`是一个函数式接口。因此，您可以使用 lambda 表达式而不是定义然后创建实现`Comparator`的类的新实例：

```
Arrays.sort(rosterAsArray,
    (Person a, Person b) -> {
        return a.getBirthday().compareTo(b.getBirthday());
    }
);
```

但是，这种比较两个`Person`实例的出生日期的方法已经存在`Person.compareByAge`。您可以在 lambda 表达式的主体中调用此方法：

```
Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);
```

因为此 lambda 表达式调用现有方法，所以可以使用方法引用而不是 lambda 表达式：

```
Arrays.sort(rosterAsArray, Person::compareByAge);
```

方法引用`Person::compareByAge`在语义上与 lambda 表达式`(a, b) -&gt; Person.compareByAge(a, b)`相同。每个都有以下特点：

*   其形式参数列表从`Comparator&lt;Person&gt;.compare`复制，即`(Person, Person)`。
*   它的主体调用方法`Person.compareByAge`。

## 各种方法引用

有四种方法引用：

| 类 | 例 |
| --- | --- |
| 引用静态方法 | `ContainingClass::staticMethodName` |
| 引用特定对象的实例方法 | `containingObject::instanceMethodName` |
| 引用特定类型的任意对象的实例方法 | `ContainingType::methodName` |
| 引用构造函数 | `ClassName::new` |

方法引用`Person::compareByAge`是对静态方法的引用。

以下是对特定对象的实例方法的引用示例：

```
class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }

    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
```

方法引用`myComparisonProvider::compareByName`调用作为对象`myComparisonProvider`一部分的方法`compareByName`。 JRE 推断出方法类型参数，在本例中是`(Person, Person)`。

以下是对特定类型的任意对象的实例方法的引用示例：

```
String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
```

方法引用`String::compareToIgnoreCase`的等效 lambda 表达式将具有形式参数列表`(String a, String b)`，其中`a`和`b`是用于更好地描述此示例的任意名称。方法引用将调用方法`a.compareToIgnoreCase(b)`。

您可以使用名称`new`以与静态方法相同的方式引用构造函数。以下方法将元素从一个集合复制到另一个集合：

```
public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>
    DEST transferElements(
        SOURCE sourceCollection,
        Supplier<DEST> collectionFactory) {

        DEST result = collectionFactory.get();
        for (T t : sourceCollection) {
            result.add(t);
        }
        return result;
}
```

函数式接口`Supplier`包含一个不带参数并返回对象的方法`get`。因此，您可以使用 lambda 表达式调用方法`transferElements`，如下所示：

```
Set<Person> rosterSetLambda =
    transferElements(roster, () -> { return new HashSet<>(); });
```

您可以使用构造函数引用代替 lambda 表达式，如下所示：

```
Set<Person> rosterSet = transferElements(roster, HashSet::new);
```

Java 编译器推断您要创建包含`Person`类型元素的`HashSet`集合。或者，您可以按如下方式指定：

```
Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
```