一个实例
使用lambda表达式可以创建简洁的匿名方法。不过,有时候lambda表达式只是简单的调用了已有的方法。此时,使用方法引用无疑是一个更简洁易读的方案。
再来看看之前使用过的Person类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class Person { public enum Sex { MALE, FEMALE } String name; LocalDate birthday; Sex gender; int age; String emailAddress; public int getAge() { return age; } public LocalDate getBirthday() { return birthday; } public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); } } |
假设所有的Person对象都保存在一个数组中,然后想按年龄对数组成员进行排序,可以使用如下的代码进行实现:
1 2 3 4 5 6 7 8 9 |
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方法的声明是这样子的:
1 |
static <t> void sort(T[] a, Comparator<? super T> c) |
注意这里的Comparator是一个函数式接口,因此无需再定义一个PersonAgeComparator类并创建一个实例,直接使用lambda表达式实现即可:
1 2 3 4 5 |
Arrays.sort(rosterAsArray, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); } ); |
不过Person类中已经有了一个compareByAge方法,因此可以对上面的表达式作进一步的简化:
1 2 3 |
Arrays.sort(rosterAsArray, (a, b) -> Person.compareByAge(a, b) ); |
因为这个lambda表达式只是调用了一个已有的方法,因此可以使用方法引用替换lambda表达式:
1 |
Arrays.sort(rosterAsArray, Person::compareByAge); |
这里的方法引用Person::compareByAge和lambda表达式(a, b) -> Person.compareByAge(a, b)在语义上是一样的。它们都有如下的特性:
- 参数列表copy自Comparator<Person>.compare方法,即(Person, Person);
- 调用了Person.compareByAge方法。
在这个例子以及下面的示例中可以看到,方法引用使用的场合大致是和lambda表达式重合的。
方法引用的类型
方法引用有四种类型:
类型 |
示例 |
静态方法引用 |
ContainingClass::staticMethodName |
实例方法引用 |
containingObject::instanceMethodName |
一个类任意对象的实例方法引用 |
ContainingType::methodName |
构造方法引用 |
ClassName::new |
静态方法引用
前面示例中的Person::compareByAge就是一个静态方法引用。
实例方法引用
下面的代码演示了实例方法引用:
1 2 3 4 5 6 7 8 9 10 11 12 |
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)。
一个类的任意对象的方法引用
如下的代码演示了一个类的任意对象的实例方法引用:
1 2 3 |
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关键字像创建静态方法引用一样创建构造方法引用。下面的代码将一个集合中的元素拷贝到了另一个集合:
1 2 3 4 5 6 7 8 |
public static <t collection ,><t extends source>, 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。这个方法没有任何参数,只是返回一个对象:
1 2 3 |
interface Supplier<t>{ T get(); } |
可以使用一个lambda表达式调用transferElements :
1 2 |
Set<person> rosterSetLambda = transferElements(roster, () -> { return new HashSet<>(); }); |
也可以使用一个构造方法引用来替换lambda表达式:
1 |
Set<person> rosterSet = transferElements(roster, HashSet::new); |
java编译器可以推断出你想创建一个HashSet集合,其中包含的元素类型是Person。当然也可以显式声明:
1 |
Set<person> rosterSet = transferElements(roster, HashSet<person>::new); |
就这样。
参考文档
https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
#################
发表评论