Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
你使用 lambda expressions 来创建匿名方法。但是,有时,lambda 表达式只会调用现有方法。在这些情况下,通过名称引用现有方法通常更清楚。方法引用使你可以这样做;对于已经有名称的方法,它们是紧凑的,易于阅读的 lambda 表达式。
再次考虑 Lambda Expressions 部分中讨论的 Person
类:
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
中查找本节中描述的代码片段):
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) -> Person.compareByAge(a, b)
相同。每种都有以下特点:
Comparator<Person>.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
调用方法 compareByName
,它是对象 myComparisonProvider
的一部分。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);