java8知識總結_2.方法引用


2.方法引用

在正式講解「方法引用」技術點前,我們先回顧下lambda表達式的基本用法。

首先lambda表達式的基本用途是用來實現函數式接口的方法。

這邊文件中要用到以下兩個java文件,我們這里先把這兩個java文件建好。

文件1:Studen類,該類包含name和age兩個屬性,一個無參構造函數,一個有參構造函數(初始化name和age);

             兩個屬性的get/set方法,一個靜態方法,一個實例方法,這兩個方法都是用來比較學生的年齡。

具體請看下面的代碼:

 1 public class Student {
 2 
 3     // 姓名
 4     private String name;
 5     // 年齡
 6     private int age;
 7 
 8     /**
 9      * 無參構造器
10      */
11     public Student() {
12 
13     }
14 
15     /**
16      * 有參構造器_初始化學生屬性
17      * 
18      * @param name
19      *            姓名
20      * @param age
21      *            年齡
22      */
23     public Student(String name, int age) {
24         this.name = name;
25         this.age = age;
26     }
27 
28     public String getName() {
29         return name;
30     }
31 
32     // ###### get/set方法 START ######
33     public void setName(String name) {
34         this.name = name;
35     }
36 
37     public int getAge() {
38         return age;
39     }
40 
41     public void setAge(int age) {
42         this.age = age;
43     }
44     // ###### get/set方法 END ######
45 
46     /**
47      * 靜態方法_比較兩個學生的年齡
48      * 
49      * @param std1
50      *            學生實例1
51      * @param std2
52      *            學生實例2
53      * @return 等於0   年齡相等
54      *         大於0   學生實例1的年齡 > 學生實例2的年齡
55      *         小於0   學生實例1的年齡 < 學生實例2的年齡
56      */
57     public static int compareAgeStatic(Student std1, Student std2) {
58         return std1.getAge() - std2.getAge();
59     }
60     
61     /**
62      * 普通方法_比較兩個學生的年齡
63      * 
64      * @param std1
65      *            學生實例1
66      * @param std2
67      *            學生實例2
68      * @return 等於0   年齡相等
69      *         大於0   學生實例1的年齡 > 學生實例2的年齡
70      *         小於0   學生實例1的年齡 < 學生實例2的年齡
71      */
72     public int compareAge(Student std1, Student std2) {
73         return std1.getAge() - std2.getAge();
74     }
75 }

文件2:CompareStudentAge接口,該接口為函數式接口,包含一個抽象方法。

具體請看下面的代碼:

1 @FunctionalInterface
2 public interface CompareStudentAge {
3 
4     /** 比較兩個學生的年齡 */
5     int compareAge(Student std1,Student std2);
6 }

下面我們用之前學習過的lambda表達式,來實現上面接口的compareAge方法。

 1         // lambda表達式實現接口的方法體
 2         CompareStudentAge csa = (Student s1, Student s2) -> {
 3             return s1.getAge() - s2.getAge();
 4         };
 5         
 6         // 聲明兩個學生實例
 7         Student std1 = new Student("yubx", 36);
 8         Student std2 = new Student("ldm", 35);
 9 
10         // 調用接口的compareAge方法
11         int result = csa.compareAge(std1, std2);
12         
13         // 打印執行結構
14         System.out.println(result);

上面是普通的lambda實現方式,執行結果:1

基於上面的兩個java文件以及對lambda表達式基本寫法的回顧,我們來學習java8的另一個特性:「方法引用」

2-1.概述

方法引用可以直接引用已有Java類或對象(實例)的方法或構造器。

方法引用的一般用途是與lambda聯合使用。

方法引用可以使語言的構造更緊湊簡潔,減少冗余代碼。

2-2.語法

類名/對象::靜態方法名/實例方法名

2-3.分類

  1. 類名::靜態方法名
  2. 對象::實例方法名
  3. 類名::實例方法名
  4. 類名::new(構造器)

2-4.實例

1.類名::靜態方法名

我們用Student類中的靜態方法compareAgeStatic來替換lambda的實現。

 1         // 「類名::靜態方法名」方法引用
 2         CompareStudentAge csa = Student::compareAgeStatic;
 3         
 4         // 聲明兩個學生實例
 5         Student std1 = new Student("yubx", 36);
 6         Student std2 = new Student("ldm", 35);
 7 
 8         // 調用接口的compareAge方法
 9         int result = csa.compareAge(std1, std2);
10         
11         // 打印執行結構
12         System.out.println(result);

執行結果:1

2.對象::實例方法名

我們用Student類中的實例方法compareAge來替換lambda的實現。

 

 1         // 構造學生類
 2         Student stdInstance = new Student();
 3         
 4         // 「對象::實例方法名」
 5         CompareStudentAge csa = stdInstance::compareAge;
 6         
 7         // 聲明兩個學生實例
 8         Student std1 = new Student("yubx", 36);
 9         Student std2 = new Student("ldm", 35);
10 
11         // 調用接口的compareAge方法
12         int result = csa.compareAge(std1, std2);
13         
14         // 打印執行結構
15         System.out.println(result);

執行結果:1

3.類名::實例方法名

這種類型,對有些小伙伴來說,可能理解起來相對比較難,我盡量說得仔細鞋。

這里我們要用到this關鍵字。

我們首先來看下this關鍵字的一種用法。

下面利用JDK的String類中的equals的代碼來進行說明。

 1     /**
 2      * Compares this string to the specified object.  The result is {@code
 3      * true} if and only if the argument is not {@code null} and is a {@code
 4      * String} object that represents the same sequence of characters as this
 5      * object.
 6      *
 7      * @param  anObject
 8      *         The object to compare this {@code String} against
 9      *
10      * @return  {@code true} if the given object represents a {@code String}
11      *          equivalent to this string, {@code false} otherwise
12      *
13      * @see  #compareTo(String)
14      * @see  #equalsIgnoreCase(String)
15      */
16     public boolean equals(Object anObject) {
17         if (this == anObject) {
18             return true;
19         }
20         if (anObject instanceof String) {
21             String anotherString = (String)anObject;
22             int n = value.length;
23             if (n == anotherString.value.length) {
24                 char v1[] = value;
25                 char v2[] = anotherString.value;
26                 int i = 0;
27                 while (n-- != 0) {
28                     if (v1[i] != v2[i])
29                         return false;
30                     i++;
31                 }
32                 return true;
33             }
34         }
35         return false;
36     }

問題:先看第17行的this關鍵字,這個this代表的是哪個實例?

想想我們平時調用equals方法的寫法:

1         String st1 = "abc";
2         String st2 = "bcd";
3         boolean ret = st1.equals(st2);

猜想:通過上面的代碼,我們大致可以猜到:JDK中的equals方法中的this指代的可能是上記代碼中的str1。

結論:java中對於實例方法,「this引用」隱式的作為第一個參數傳遞進去,並且默認用這個this調用該實例方法。

(這種調用方式和python的方法調用很像,有興趣的小伙伴可以去查閱下相關資料。)

方法引用就利用了上面的原理,可以實現「類名::實例方法名」的方式。

綜上,我們在上面的Studen類中追加如下實例方法:

    /**
     * 普通方法_比較兩個學生的年齡
     * 
     * @param std
     *            學生實例
     * @return 等於0   年齡相等
     *         大於0   學生實例1的年齡 > 學生實例2的年齡
     *         小於0   學生實例1的年齡 < 學生實例2的年齡
     */
    public int compareAge(Student std) {
        return this.getAge() - std.getAge();
    }

上面的this關鍵字指代的就是方法接口中的第一個參數。

這時,我們用「類名::實例方法名」的方式,重寫lambda的實現。

 1         // 「類名::實例方法名」
 2         CompareStudentAge csa = Student::compareAge;
 3         
 4         // 聲明兩個學生實例
 5         Student std1 = new Student("yubx", 36);
 6         Student std2 = new Student("ldm", 35);
 7         
 8         // 調用接口的compareAge方法
 9         int result = csa.compareAge(std1, std2);
10         
11         // 打印執行結構
12         System.out.println(result);

上面的代碼,實際上用的就是std1去調用只有一個參數的實例方法compareAge。

執行結果:1

注:如果小伙伴們對上面的代碼處理邏輯,理解上還是不夠清晰的話,

建議自己多動手去寫些傳統代碼,加深對this隱式傳遞處理方式的理解。

4.類名::new(構造器)

首先我們假想一個情景:

・有一個函數式接口,該接口的抽象方法接受學生姓名和學生你年齡做參數。

(和Student類中有參構造函數的參數列表一致)

・在Student類中追加一個實例方法,該方法接受一個默認this實例引用參數和一個int類型參數(要增加的年齡)

    

1 @FunctionalInterface
2 public interface ModifyStudent {
3 
4     /** 修改學生的年齡 */
5     Student modify(String str,int i);
6 }
 1     /**
 2      * 修改yubx的年齡
 3      * 
 4      * @param addAge
 5      *            增加的年齡
 6      * @return 該學生的實例
 7      */
 8     public Student modify(int addAge) {
 9         if (!this.name.equals("yubx")) {
10             System.out.println("yubx以外的學生不能修改Ta的年齡!");
11             return this;
12         }
13         this.setAge(this.age + addAge);
14         return this;
15     }

基於上面的代碼,我們用「類名::new(構造器)」的方式重寫lambda的實現。

 1         // 「類名::new(構造函數)」
 2         ModifyStudent student = Student::new;
 3         
 4         // 調用接Student類中的compareAge方法
 5         Student newStd = student.modify("yubx", 36);
 6         // 調用Student類中的modify方法
 7         newStd.modify(10);
 8         
 9         // 打印執行結構
10         System.out.println("學生"+newStd.getName()+"的年齡是" +newStd.getAge());

執行結果:學生yubx的年齡是46

我們把上面第5行的參數列表修改下:

 1         // 「類名::new(構造函數)」
 2         ModifyStudent student = Student::new;
 3         
 4         // 調用接Student類中的compareAge方法
 5 //        Student newStd = student.modify("yubx", 36);
 6         Student newStd = student.modify("ldm", 35);
 7         // 調用Student類中的modify方法
 8         newStd.modify(10);
 9         
10         // 打印執行結構
11         System.out.println("學生"+newStd.getName()+"的年齡是" +newStd.getAge());

執行結果:

yubx以外的學生不能修改Ta的年齡!

學生ldm的年齡是35

2-5.綜上小結

上面我們用具體實例結合lambda表達式,學習了方法引用的四種方式,從中我們總結一個結論:

使用「方法引用」時,要引用的方法(也就是操作符「::」后的方法)的參數列表,

必須要與lambda表達式要實現的函數式接口的方法參數列表一致。

說到底,lambda表達式的住喲用途:實現函數式接口的抽象方法。

而「方法引用」就是用來替換lambda表達式的"方法體"(具體實現)。

以上,希望還沒理解吃透的小伙伴,查找以下其他資料,多多練習。如果有心得,歡迎底下留言交流!

PS:想應用實例或應用場景太費腦細胞~~!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM