Java 方法引用(翻譯自官方文檔 JDK 8 tutorial)

方法引用(Method Reference)

你可能會使用 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表達式相同。每一個方法引用都有如下特徵:對象

  1. 它的形參是從Comparator<Person>.compare(Person,Person)中複製的;
  2. 它的方法體調用了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),其中ab是用於更好地描述此示例的任意名稱。這個方法引用將會調用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);
複製代碼
相關文章
相關標籤/搜索