1. 程式人生 > >Java四種方法引用

Java四種方法引用

方法引用是lambda表示式的一種特殊形式,如果正好有某個方法滿足一個lambda表示式的形式,那就可以將這個lambda表示式用方法引用的方式表示,但是如果這個lambda表示式的比較複雜就不能用方法引用進行替換。實際上方法引用是lambda表示式的一種語法糖。
在介紹方法引用使用方式之前,先將方法引用分下類
方法引用共分為四類:
1.類名::靜態方法名
2.物件::例項方法名
3.類名::例項方法名 
4.類名::new

首先來看下第一種 類名::靜態方法名 為了演示我們自定義了一個Student類

public class Student {
    private String name;
    private
int score; public Student(){ } public Student(String name,int score){ this.name = name; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore
()
{ return score; } public void setScore(int score) { this.score = score; } public static int compareStudentByScore(Student student1,Student student2){ return student1.getScore() - student2.getScore(); } }

Student類有兩個屬性name和score並提供了初始化name和score的構造方法,並且在最下方提供了兩個靜態方法分別按score和name進行比較先後順序。
接下來的需求是,按著分數由小到大排列並輸出,在使用方法引用前,我們先使用lambda表示式的方式進行處理

Student student1 = new Student("zhangsan",60);
Student student2 = new Student("lisi",70);
Student student3 = new Student("wangwu",80);
Student student4 = new Student("zhaoliu",90);
List<Student> students = Arrays.asList(student1,student2,student3,student4);

students.sort((o1, o2) -> o1.getScore() - o2.getScore());
students.forEach(student -> System.out.println(student.getScore()));

sort方法接收一個Comparator函式式介面,介面中唯一的抽象方法compare接收兩個引數返回一個int型別值,下方是Comparator介面定義

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
}

我們再看下Student類中定義的compareStudentByScore靜態方法

public static int compareStudentByScore(Student student1,Student student2){
    return student1.getScore() - student2.getScore();
}

同樣是接收兩個引數返回一個int型別值,而且是對Student物件的分數進行比較,所以我們這裡就可以 使用類名::靜態方法名 方法引用替換lambda表示式

students.sort(Student::compareStudentByScore);
students.forEach(student -> System.out.println(student.getScore()));

第二種 物件::例項方法名
我們再自定義一個用於比較Student元素的類

public class StudentComparator {
    public int compareStudentByScore(Student student1,Student student2){
        return student2.getScore() - student1.getScore();
    }
}

StudentComparator中定義了一個非靜態的,例項方法compareStudentByScore,同樣該方法的定義滿足Comparator介面的compare方法定義,所以這裡可以直接使用 物件::例項方法名 的方式使用方法引用來替換lambda表示式

StudentComparator studentComparator = new StudentComparator();
students.sort(studentComparator::compareStudentByScore);
students.forEach(student -> System.out.println(student.getScore()));

第三種 類名::例項方法名 
這種方法引用的方式較之前兩種稍微有一些不好理解,因為無論是通過類名呼叫靜態方法還是通過物件呼叫例項方法這都是符合Java的語法,使用起來也比較清晰明瞭。那我們帶著這個疑問來了解一下這個比較特殊的方法引用。
現在再看一下Student類中靜態方法的定義

public static int compareStudentByScore(Student student1,Student student2){
    return student1.getScore() - student2.getScore();
}

雖然這個方法在語法上沒有任何問題,可以作為一個工具正常使用,但是有沒有覺得其在設計上是不合適的或者是錯誤的。這樣的方法定義放在任何一個類中都可以正常使用,而不只是從屬於Student這個類,那如果要定義一個只能從屬於Student類的比較方法下面這個例項方法更合適一些

public int compareByScore(Student student){
    return this.getScore() - student.getScore();
}

接收一個Student物件和當前呼叫該方法的Student物件的分數進行比較即可。現在我們就可以使用 類名::例項方法名 這種方式的方法引用替換lambda表示式了

students.sort(Student::compareByScore);
students.forEach(student -> System.out.println(student.getScore()));

這裡非常奇怪,sort方法接收的lambda表示式不應該是兩個引數麼,為什麼這個例項方法只有一個引數也滿足了lambda表示式的定義(想想這個方法是誰來呼叫的)。這就是 類名::例項方法名 這種方法引用的特殊之處:當使用 類名::例項方法名 方法引用時,一定是lambda表示式所接收的第一個引數來呼叫例項方法,如果lambda表示式接收多個引數,其餘的引數作為方法的引數傳遞進去。
結合本例來看,最初的lambda表示式是這樣的

students.sort((o1, o2) -> o1.getScore() - o2.getScore());

那使用 類名::例項方法名 方法引用時,一定是o1來呼叫了compareByScore例項方法,並將o2作為引數傳遞進來進行比較。是不是就符合了compareByScore的方法定義。

第四種 類名::new
也稱構造方法引用,和前兩種類似只要符合lambda表示式的定義即可,回想下Supplier函式式介面的get方法,不接收引數有返回值,正好符合無參構造方法的定義
@FunctionalInterface
public interface Supplier<T> {

/**
 * Gets a result.
 *
 * @return a result
 */
T get();

}

Supplier<Student> supplier = Student::new;

上面就是使用了Student類構造方法引用建立了supplier例項,以後通過supplier.get()就可以獲取一個Student型別的物件,前提是Student類中存在無參構造方法。