java8允许我们使用lambda表达式创建匿名方法。但有时lambda表达式除了调用现有方法之外什么也不做。在这些情况下,通过名称引用现有的方法,通常能更直白的表现出方法的调用过程。对于已经存在的且具有方法名称的方法,它其实是简洁且易于读取的一种lambda表达式,或者说是对lambda表达式的一种进一步简化。
现在我们来看看下面这个“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);
}}
假设你的社交网络应用程序的成员包含在一个数组中,并且你希望按年龄对数组进行排序。你可以使用以下代码:
Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);
class PersonAgeComparator implements Comparator {
public int compare(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
Arrays.sort(rosterAsArray, new PersonAgeComparator());
上面最后一行代码:中的sort方法源码如下:
static void sort(T[] a, Comparator super T> c)
注意,接口comparator是一个功能接口。因此,你可以使用lambda表达式,而不是定义并创建实现comparator的类的新实例,所以上上面的代码可以用lambda表达式改写成下面这样:
Arrays.sort(rosterAsArray,
(Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
}
);
但是,我们已经在Person bean对象中提前写好了用来比较两个person对象的出生日期的方法:,所以你其实可以在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)相同。他们都有以下特点:
(Person, Person)
。有四种方法引用:
种类 |
案例 |
---|---|
引用静态方法 | ContainingClass::staticMethodName |
对特定对象的实例方法的引用 | containingObject::instanceMethodName |
对特定类型的任意对象的实例方法的引用 | ContainingType::methodName |
对构造函数的引用 | ClassName::new |
例如:
Person::comparebyage 是对Person类的静态方法 comparebyage 的引用。
2、引用特定对象的实例方法
以下是对特定对象的实例方法的引用示例:
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::compareTogignoreCase 的等效lambda表达式会有形参列表(String a,String b),其中a和b是用于更好地描述此示例的任意名称。这次的方法引用,将调用方法a.CompareTognoreCase(b)。
这里官网原文可能说的不是很清楚,也有点拗口,多说一句:
如何理解:对特定类型的 任意对象的 实例方法的引用 这句话呢?
实际上上面代码没有在特定实例(也就是没有new一个指定的String 实例出来,像这样:String s = new String() ,s就是一个指定的String对象的实例)上引用方法 compareTogignoreCase ,而是在String类自身引用的。
我们进入Arrays.sort(xx,xx)方法源码,我们可以看到这个数组排序方法底层算法涉及循环,那么这行代码我们可以想象它会在每循环到数组的一个String元素时,以这个String元素所属类型对象(其实就是String对象)的身份去调用compareTogignoreCase方法,即:
会调用的compareTogignoreCase --》String Barbara = new String(); Barbara.compareTogignoreCase
会调用的compareTogignoreCase --》String James= new String();James.compareTogignoreCase
以此类推。。。
我可能解释的也不太好,主要就是想语言上理解一下:对特定类型的 任意对象的 实例方法 的引用
你可以通过使用name new 的方式引用构造函数,这与静态方法的引用方式类似。
下面这个示例方法,是将元素从一个集合复制到另一个集合。我们将以这个方法为例,讲解如何使用方法引用的方式引用构造函数。
public static , DEST extends Collection>
DEST transferElements(SOURCE sourceCollection,Supplier collectionFactory) {
DEST result = collectionFactory.get();
for (T t : sourceCollection) {
result.add(t);
}
return result;
}
函数接口supplier包含一个无参但返回一个对象的get方法,源码可见,他返回的类型就是泛型的类型,如下:
因此,可以使用lambda表达式调用方法transferElements,如下所示:
Set rosterSetLambda =
transferElements(roster, () -> { return new HashSet<>(); });
可以使用构造函数引用代替lambda表达式,如下所示:
Set rosterSet = transferElements(roster, HashSet::new);
上面代码,Java编译器执行的时候会自动推断出这里想要创建一个包含Person类型元素的hashset集合。当然你也可以显示的指定类型,如下:
Set rosterSet = transferElements(roster, HashSet::new);
Java8新特性1:lambda表达式入门--由浅入深,从单发步枪迈向自动步枪
Java8新特性2:方法引用--深入理解双冒号::的使用
Java8新特性3:Stream1——什么是Stream,Stream的特性,如何使用Stream,Stream与Collection集合的区别
Java8新特性3:Stream2—一文详解Stream API,让你快速理解Stream Api提供的诸多常用方法
Java8新特性3:Stream3—数值流与对象流的转化及其方法使用