你可能會使用 Lamada 表達式來建立一個匿名方法,但有些時候,一個 Lamada 表達式除了調用一個現有的方法以外自己什麼事情也不作。在這種狀況下,經過方法名稱來引用現有方法的方式一般會使想要表達的意思更清晰。方法引用使您能夠執行此操做。方法引用是一個更加緊湊,易讀的Lambda表達式,注意方法引用是一個Lambda表達式,其中方法引用的操做符是雙冒號"::"。數組
考慮有以下的Person
類:bash
public class Person {
public enum Sex {
MALE, FEMALE
}
String name; //姓名
LocalDate birthday;//生日LocalDate實現了Comparable接口
Sex gender;//性別
String emailAddress;//郵件地址
Person(String nameArg, LocalDate birthdayArg,
Sex genderArg, String emailArg) {
name = nameArg;
birthday = birthdayArg;
gender = genderArg;
emailAddress = emailArg;
}
public int getAge() {
return birthday
.until(IsoChronology.INSTANCE.dateNow())
.getYears();
}
public void printPerson() {
System.out.println(name + ", " + this.getAge());
}
public Sex getGender() {
return gender;
}
public String getName() {
return name;
}
public String getEmailAddress() {
return emailAddress;
}
public LocalDate getBirthday() {
return birthday;
}
//靜態方法
public static int compareByAge(Person a, Person b) {
return a.birthday.compareTo(b.birthday);
}
public static List<Person> createRoster() {//roster是一個集合
List<Person> roster = new ArrayList<>();
roster.add(
new Person(
"Fred",
IsoChronology.INSTANCE.date(1980, 6, 20),
Person.Sex.MALE,
"fred@example.com"));
roster.add(
new Person(
"Jane",
IsoChronology.INSTANCE.date(1990, 7, 15),
Person.Sex.FEMALE, "jane@example.com"));
roster.add(
new Person(
"George",
IsoChronology.INSTANCE.date(1991, 8, 13),
Person.Sex.MALE, "george@example.com"));
roster.add(
new Person(
"Bob",
IsoChronology.INSTANCE.date(2000, 9, 12),
Person.Sex.MALE, "bob@example.com"));
return roster;
}
@Override
public String toString() {
return "Person [name=" + name + ", birthday=" + birthday + ", gender=" + gender + ", emailAddress="
+ emailAddress + "]";
}
}
複製代碼
假設你的通信錄成員包含在一個數組中,而且您但願按年齡對數組進行排序,傳統的方法你可能會這樣作(這裏只是一個代碼片斷)ide
//roster是一個集合
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進行排序
Arrays.sort(rosterAsArray, new PersonAgeComparator());
複製代碼
其中,sort()
方法的簽名以下:函數
static <T> void sort(T[] a, Comparator<? super T> c)
ui
注意到接口Comparator
是一個函數式接口,所以你可使用一個Lamada表達式來代替,而不須要實例化一個Comparator
的子類。this
Arrays.sort(rosterAsArray,
(Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
}
);
複製代碼
儘管如此,Person.compareByAge
方法已經實現了比較兩個Person
實例的生日大小的功能,所以你能夠調用該方法來替換Lamada表達式的方法體,以下:spa
Arrays.sort(rosterAsArray,
(a, b) -> Person.compareByAge(a, b)
);
複製代碼
由於這個Lamada表達式調用了一個已經存在的方法(即Person.compareByAge
方法),所以你能夠經過方法引用來代替Lamada表達式自己,以下:code
Arrays.sort(rosterAsArray, Person::compareByAge);
複製代碼
Person::compareByAge
這個方法引用在語義上與Lamada表達式相同。每一個方法引用都有如下特徵:對象
Comparator<Person>.compare
即(Person,Person)
中複製的;Person.compareByAge
方法;如下是四種方法引用:排序
種類 | 例子 |
---|---|
引用一個靜態方法 | ContainingClass::staticMethodName |
引用特定對象的實例方法 | containingObject::instanceMethodName |
引用特定類型的任意對象的實例方法 | ContainingType::methodName (這個還真的有點難理解,歡迎評論區討論) |
引用一個構造方法 | ClassName::new |
Person::compareByAge
這個方法引用就是引用一個靜態方法。
請看下面的例子:
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 :: compareToIgnoreCase
這個方法引用的等效lambda表達式將具備形參列表(String a,String b)
,其中a
和b
是用於更好地描述此示例的任意名稱。這個方法引用將會調用a.compareToIgnoreCase(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
方法,因此你可使用Lamada表達式調用transferElements
方法,以下:
Set<Person> rosterSetLambda =
transferElements(roster, () -> { return new HashSet<>(); });
複製代碼
你可使用一個構造器引用替換上面的Lamada表達式:
Set<Person> rosterSet = transferElements(roster, HashSet::new);
複製代碼
Java JRE會推斷你想要去建立一個Person
類型的HashSet
集合,或者,您能夠以下指定:
Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
複製代碼