概述
在學(xué)習lambda表達式之后,我們通常使用lambda表達式來創(chuàng)建匿名方法。然而,有時候我們僅僅是調(diào)用了一個已存在的方法。如下:
Arrays.sort(stringsArray,(s1,s2)->s1.compareToIgnoreCase(s2));
在Java8中,我們可以直接通過方法引用來簡寫lambda表達式中已經(jīng)存在的方法。
Arrays.sort(stringsArray, String::compareToIgnoreCase);
這種特性就叫做方法引用(Method Reference)。
什么是方法引用?
方法引用是用來直接訪問類或者實例的已經(jīng)存在的方法或者構(gòu)造方法。方法引用提供了一種引用而不執(zhí)行方法的方式,它需要由兼容的函數(shù)式接口構(gòu)成的目標類型上下文。計算時,方法引用會創(chuàng)建函數(shù)式接口的一個實例。
當Lambda表達式中只是執(zhí)行一個方法調(diào)用時,不用Lambda表達式,直接通過方法引用的形式可讀性更高一些。方法引用是一種更簡潔易懂的Lambda表達式。
注意方法引用是一個Lambda表達式,其中方法引用的操作符是雙冒號"::"。
簡單地說,就是一個Lambda表達式。在Java 8中,我們會使用Lambda表達式創(chuàng)建匿名方法,但是有時候,我們的Lambda表達式可能僅僅調(diào)用一個已存在的方法,而不做任何其它事,對于這種情況,通過一個方法名字來引用這個已存在的方法會更加清晰,Java 8的方法引用允許我們這樣做。方法引用是一個更加緊湊,易讀的Lambda表達式,注意方法引用是一個Lambda表達式,其中方法引用的操作符是雙冒號"::"
方法例子
首先定義一個Person類,如下:
import java.time.LocalDate;
public class Person {
public Person(String name, LocalDate birthday) {
this.name = name;
this.birthday = birthday;
}
String name;
LocalDate birthday;
public LocalDate getBirthday() {
return birthday;
}
public static int compareByAge(Person a, Person b) {
return a.birthday.compareTo(b.birthday);
}
@Override
public String toString() {
return this.name;
}
}
假設(shè)我們有一個Person數(shù)組,并且想對它進行排序,這時候,我們可能會這樣寫:
原始寫法,使用匿名類:
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Comparator;
import org.junit.Test;
import com.demo.model.Person;
public class testMethodReference {
@Test
public void test() {
Person[] pArr = new Person[]{
new Person("003", LocalDate.of(2016,9,1)),
new Person("001", LocalDate.of(2016,2,1)),
new Person("002", LocalDate.of(2016,3,1)),
new Person("004", LocalDate.of(2016,12,1))};
// 使用匿名類
Arrays.sort(pArr, new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
});
System.out.println(Arrays.asList(pArr));
}
}
其中,Arrays類的sort方法定義如下:
public static <T> void sort(T[] a, Comparator<? super T> c)
這里,我們首先要注意Comparator接口是一個函數(shù)式接口,因此我們可以使用Lambda表達式,而不需要定義一個實現(xiàn)Comparator接口的類,并創(chuàng)建它的實例對象,傳給sort方法。
使用Lambda表達式,我們可以這樣寫:
改進一,使用Lambda表達式,未調(diào)用已存在的方法
@Test
public void test1() {
Person[] pArr = new Person[]{
new Person("003", LocalDate.of(2016,9,1)),
new Person("001", LocalDate.of(2016,2,1)),
new Person("002", LocalDate.of(2016,3,1)),
new Person("004", LocalDate.of(2016,12,1))};
//使用lambda表達式
Arrays.sort(pArr, (Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
});
System.out.println(Arrays.asList(pArr));
}
然而,在以上代碼中,關(guān)于兩個人生日的比較方法在Person類中已經(jīng)定義了,因此,我們可以直接使用已存在的Person.compareByAge方法。
改進二,使用Lambda表達式,調(diào)用已存在的方法
@Test
public void test2() {
Person[] pArr = new Person[]{
new Person("003", LocalDate.of(2016,9,1)),
new Person("001", LocalDate.of(2016,2,1)),
new Person("002", LocalDate.of(2016,3,1)),
new Person("004", LocalDate.of(2016,12,1))};
//使用lambda表達式和類的靜態(tài)方法
Arrays.sort(pArr, (a ,b) -> Person.compareByAge(a, b));
System.out.println(Arrays.asList(pArr));
}
因為這個Lambda表達式調(diào)用了一個已存在的方法,因此,我們可以直接使用方法引用來替代這個Lambda表達式。
改進三,使用方法引用
@Test
public void test3() {
Person[] pArr = new Person[]{
new Person("003", LocalDate.of(2016,9,1)),
new Person("001", LocalDate.of(2016,2,1)),
new Person("002", LocalDate.of(2016,3,1)),
new Person("004", LocalDate.of(2016,12,1))};
//使用方法引用,引用的是類的靜態(tài)方法
Arrays.sort(pArr, Person::compareByAge);
System.out.println(Arrays.asList(pArr));
}
運行結(jié)果:
[001, 002, 003, 004]
在以上代碼中,方法引用Person::compareByAge在語義上與Lambda表達式 (a, b) -> Person.compareByAge(a, b) 是等同的,都有如下特性:
- 真實的參數(shù)是拷貝自Comparator<Person>.compare方法,即(Person, Person);
- 表達式體調(diào)用Person.compareByAge方法。
四種方法引用
方法引用的標準形式是:類名::方法名。(注意:只需要寫方法名,不需要寫括號)
有以下四種形式的方法引用:
| 類型 | 示例 |
|---|---|
| 引用靜態(tài)方法 | ContainingClass::staticMethodName |
| 引用某對象的實例方法 | containingObject::instanceMethodName |
| 引用某類型的任意對象的實例方法 | ContainingType::methodName |
| 引用構(gòu)造方法 | ClassName::new |
舉列:
1. 靜態(tài)方法
組成語法格式:ClassName::staticMethodName
我們前面舉的例子Person::compareByAge就是一個靜態(tài)方法引用。
注意:
- 靜態(tài)方法引用比較容易理解,和靜態(tài)方法調(diào)用相比,只是把 . 換為 ::
- 在目標類型兼容的任何地方,都可以使用靜態(tài)方法引用。
例子:
String::valueOf 等價于lambda表達式 (s) -> String.valueOf(s)
Math::pow 等價于lambda表達式 (x, y) -> Math.pow(x, y);