这些Java教程是针对JDK 8编写的。本页中描述的示例和实践不利用后续版本引入的改进,并可能使用不再可用的技术。
请参阅Java语言变更了解Java SE 9及后续版本中更新的语言特性的摘要。
请参阅JDK发行说明了解所有JDK版本的新功能、增强功能以及删除或弃用选项的信息。
你可以使用lambda表达式来创建匿名方法。然而,有时候lambda表达式只是调用一个已经存在的方法。在这种情况下,通过引用已存在的方法来引用它往往更清晰。方法引用使您可以做到这一点;它们是紧凑且易读的lambda表达式,用于已经有名称的方法。
public class Person { // ... LocalDate birthday; public int getAge() { // ... } public LocalDate 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 |
Person::compareByAge MethodReferencesExamples::appendStrings |
特定对象的实例方法引用 | containingObject::instanceMethodName |
myComparisonProvider::compareByName myApp::appendStrings2 |
特定类型的任意对象的实例方法引用 | ContainingType::methodName |
String::compareToIgnoreCase String::concat |
构造函数引用 | ClassName::new |
HashSet::new |
下面的示例,MethodReferencesExamples
,包含了前三种方法引用的示例:
import java.util.function.BiFunction; public class MethodReferencesExamples { public static <T> T mergeThings(T a, T b, BiFunction<T, T, T> merger) { return merger.apply(a, b); } public static String appendStrings(String a, String b) { return a + b; } public String appendStrings2(String a, String b) { return a + b; } public static void main(String[] args) { MethodReferencesExamples myApp = new MethodReferencesExamples(); // 用lambda表达式调用mergeThings方法 System.out.println(MethodReferencesExamples. mergeThings("Hello ", "World!", (a, b) -> a + b)); // 静态方法引用 System.out.println(MethodReferencesExamples. mergeThings("Hello ", "World!", MethodReferencesExamples::appendStrings)); // 特定对象的实例方法引用 System.out.println(MethodReferencesExamples. mergeThings("Hello ", "World!", myApp::appendStrings2)); // 特定类型的任意对象的实例方法引用 System.out.println(MethodReferencesExamples. mergeThings("Hello ", "World!", String::concat)); } }
所有的System.out.println()
语句都会打印相同的内容:Hello World!
BiFunction
是java.util.function
包中的众多函数式接口之一。 BiFunction
函数式接口可以表示接受两个参数并产生结果的lambda表达式或方法引用。
Person::compareByAge
和MethodReferencesExamples::appendStrings
这两个方法引用是对静态方法的引用。
以下是对特定对象的实例方法的引用的示例:
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)
。
类似地,方法引用myApp::appendStrings2
调用了对象myApp
的appendStrings2
方法。JRE推断出了方法的类型参数,本例中为(String, String)
。
以下是对特定类型的任意对象的实例方法的引用的示例:
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)
方法。
类似地,方法引用String::concat
将调用a.concat(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);