Java8-8-方法引用詳解

上一篇咱們詳細介紹了Optional類用來避免空指針問題,本篇咱們全面瞭解一下Java8中的方法引用特性。
方法引用是lambda表達式的一種特殊形式,若是正好有某個方法知足一個lambda表達式的形式,那就能夠將這個lambda表達式用方法引用的方式表示,可是若是這個lambda表達式的比較複雜就不能用方法引用進行替換。實際上方法引用是lambda表達式的一種語法糖。
在介紹方法引用使用方式以前,先將方法引用分下類
方法引用共分爲四類:
1.類名::靜態方法名
2.對象::實例方法名
3.類名::實例方法名
4.類名::new編程

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

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();
    }

    public static int compareStudentByName(Student student1,Student student2){
        return student1.getName().compareToIgnoreCase(student2.getName());
    }
}

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

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表達式this

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類中存在無參構造方法。

小結:本篇全面介紹了方法引用的四種使用方式,且每種方式都有對應一個示例來幫助你們理解。當咱們使用lambda表達式進行函數式編程時,若是某個方法正好知足lambda的定義,也知足實際需求的邏輯,就可使用方法引用的方式來替換lambda表達式。接下來咱們將真正開始學習stream api,並結合前面學習的內容體驗stream api的強大之處。

下一篇

相關文章
相關標籤/搜索