JDK8新特性-方法引用
概述
方法引用是特定Lamda表达式的一种简写,其思路就是能替换Lamda表达式就直接调用函数使用方法名。
其语法格式:类名 :: 方法名。
什么是方法引用
方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会创建函数式接口的一个实例。
当Lambda表达式中只是执行一个方法调用时,不用Lambda表达式,直接通过方法引用的形式可读性更高一些。方法引用是一种更简洁易懂的Lambda表达式。
注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号”::”。
简单地说,就是一个Lambda表达式。在Java 8中,我们会使用Lambda表达式创建匿名方法,但是有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰,Java 8的方法引用允许我们这样做。方法引用是一个更加紧凑,易读的Lambda表达式,注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号”::”。
方法引用的使用场景
我们用Lambda表达式来实现匿名方法。但有些情况下,我们用Lambda表达式仅仅是调用一些已经存在的方法,除了调用动作外,没有其他任何多余的动作,在这种情况下,我们倾向于通过方法名来调用它,而Lambda表达式可以帮助我们实现这一要求,它使得Lambda在调用那些已经拥有方法名的方法的代码更简洁、更容易理解。方法引用可以理解为Lambda表达式的另外一种表现形式。
方法引用的分类
类型 | 语法 | 对应的Lambda表达式 |
---|---|---|
静态方法引用 | 类名::staticMethod | (args) -> 类名.staticMethod(args) |
实例方法引用 | inst::instMethod | (args) -> inst.instMethod(args) |
对象方法引用 | 类名::instMethod | (inst,args) -> 类名.instMethod(args) |
构建方法引用 | 类名::new | (args) -> new 类名(args) |
方法引用举例
静态方法引用
有一个Person类,如下所示:
@Data
public class Person {
private String name;
private Integer age;
public static int compareByAge(Person a, Person b) {
return a.age.compareTo(b.age);
}
}
现假设,一个部门有30人,把他们存放在一个数组中,并按年龄排序,通常我们可以自己写一个比较器,代码如下:
Person[] rosterAsArray = new Person[30];
// 添加数组元素省略
class PersonAgeComparator implements Comparator<Person> {
public int compare(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
Arrays.sort(rosterAsArray, new PersonAgeComparator());
Arrays.sort的声明为:public staticvoid sort(T[] a, Comparator<? super T> c),比较器参数Comparator为一个函数式接口,利用上一节Lambda表达式所学知识,可以改写为以下代码:
Person[] rosterAsArray = new Person[30];
// 添加数组元素省略
Arrays.sort(rosterAsArray, (a,b) -> a.getAge().compareTo(b.getAge()));
然而,你会发现,Perdon类中已经有了一个静态方法的比较器:compareByAge,因此,我们改用Person类已经提供的比较器:
Person[] rosterAsArray = new Person[30];
// 添加数组元素省略
Arrays.sort(rosterAsArray, (a,b) -> Person.compareByAge(a,b));
以上代码,因为Lambda表达式调用了一个已经存在的静态方法,根据我们第2节表格中的语法,上面的代码可以最终改写成静态方法引用:
Person[] rosterAsArray = new Person[30];
// 添加数组元素省略
Arrays.sort(rosterAsArray, Person::compareByAge);
下面这个例子更简单:
public class Test {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(82,22,34,50,9);
list.sort(Integer::compare);
System.out.println(list);
}
}
对一个Integer列表进行排序,因为Integer中已经存在静态的比较方法compare(),因此可以直接用静态方法引用的方式来调用 ,运行结果为:
[9, 22, 34, 50, 82]
实例方法引用
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
String的equals()方法:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
public static void main(String[] args) {
BiPredicate<String,String> bp = (x, y) -> x.equals(y);
BiPredicate<String,String> bp1 = String::equals;
boolean test = bp1.test("xy", "xx");
System.out.println(test);
}
BiPredicate的test()方法接受两个参数,x和y,具体实现为x.equals(y),满足Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数,因此可以使用对象方法引用。
构造方法引用
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致。
如:要获取一个空的User列表:
Supplier<List<User>> userSupplier = () -> new ArrayList<>();
List<User> user = userSupplier.get();
Supplier<List<User>> userSupplier2 = ArrayList<User>::new; // 构造方法引用写法
List<User> user2 = userSupplier.get();