在學習lambda表達式之后,我們通常使用lambda表達式來創建匿名方法。然而,有時候我們僅僅是調用了一個已存在的方法。如下:
Arrays.sort(stringsArray,(s1,s2)->s1.compareToIgnoreCase(s2));
在Java8中,我們可以直接通過方法引用來簡寫lambda表達式中已經存在的方法。
Arrays.sort(stringsArray, String::compareToIgnoreCase);
這種特性就叫做方法引用(Method Reference)。
方法引用通過方法的名字來指向一個方法。
方法引用可以使語言的構造更緊湊簡潔,減少冗余代碼。
方法引用使用一對冒號 ::
方法引用的標準形式是:類名::方法名。(注意:只需要寫方法名,不需要寫括號)
有以下四種形式的方法引用,用的最多的應該就是第三種:
| 類型 | 示例 |
| 引用靜態方法 | ContainingClass::staticMethodName |
| 引用某個對象的實例方法 | containingObject::instanceMethodName |
| 引用某個類型的任意對象的實例方法 | ContainingType::methodName |
| 引用構造方法 | ClassName::new |
package jdk8.lambda; import java.time.LocalDate; import java.util.Arrays;
import java.util.function.Function;
import java.util.function.Supplier; /** * jdk8.lambda * * @Description: * @author: changliu * @Date: 2019/7/31 */ public class refer { /** * 實體類-人 */ public static class Person{ public Person(String name, LocalDate birthday) { this.name = name; this.birthday = birthday; }
public Person(String name){
this.name = name;
}
public Person(){
} private String name; private LocalDate birthday; public String getName(){
return name;
}
public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); } } public static void main(String[] args) { Person[] pArr = new Person[]{ new Person("003", LocalDate.of(2016,9,1)), new Person("001", LocalDate.of(2016,2,1)), new Person("002", LocalDate.of(2016,3,1)), new Person("004", LocalDate.of(2016,12,1))}; //使用lambda表達式和類的靜態方法 Arrays.sort(pArr, (a , b) -> Person.compareByAge(a, b)); //使用方法引用,引用的是類的靜態方法 Arrays.sort(pArr, Person::compareByAge); System.out.println(Arrays.asList(pArr));
String var = "abcd";
//使用方法引用,引用某個對象的實例方法
Function<Integer, Character> biFunction = var::charAt;
//lambda寫法
Function<Integer,Character> biLambdaFun = index -> var.charAt(index);
System.out.println(biFunction.apply(3));
Person p = new Person("小命",LocalDate.of(2016,9,1));
//使用方法引用,引用某個類型的任意對象的實例方法
Function<Person,String> pf = Person::getName;
//lambda寫法
Function<Person,String> pfLam = person -> person.getName();
System.out.println(pf.apply(p));
//使用方法引用,引用構造方法(無參)
Supplier<Person> sp = Person::new;
//使用方法引用,引用構造方法(帶參)
Function<String,Person> fsp = Person::new;
//lambda寫法
Function<String,Person> fspLam = name -> new Person(name);
System.out.println(sp.get());
System.out.println(fsp.apply("張三").getName());
} }
這個例子中紅色標注的是使用了方法引用,通過對比lambda寫法,可以看出更為簡潔,同樣也能看出方法引用僅在特定場合下可以使用,假設是用lambda做一系列的邏輯,那么方法引用就不合適了。
接下來方法引用還有個重要的特性需要知曉,那就是使用方法引用則隱含了引入的外部變量一定是不變的,所以可以不用加final修飾,也不像lambda引入的外部變量是隱含的final修飾,換句話說,只要可以用方法引用,那么引入的外部變量是可以隨時修改指向的。這個地方應該怎么理解呢?接下來我們舉個??

從上圖可以看出,方法引用idea并沒有提示我們有誤,而lambda表達式提示有誤,而且錯誤指的是var應該由final修飾或者符合final修飾。
那么前面有說過“方法引用”實際是lambda表達式的一種特定情況下的寫法,并且更加精簡。所以“方法引用”不應該遵循lambda表達式的特點以及限定嗎?答案是確定的,“方法引用”確實要遵循,但是通過總結“方法引用”的四種表達方式我們可以發現一個現象,即“方法引用”因為固定的場景及寫法,所以一旦用上了“方法引用”,是無法修改引入變量的地址的,可以確定引入變量在外部和內部均指向同一個地址。
這里又牽扯出另一個問題,為什么lambda要求引入的變量必須要是final修飾的?這是因為lambda本身就屬于匿名內部類,所以遵循匿名內部類使用外部變量的要求。那么匿名內部類為什么要求外部變量是final修飾呢?簡單的來說,是為了保證局部變量和內部類中復制品 的數據一致性,如果想了解為啥,可以看看這個https://blog.csdn.net/u011617742/article/details/51613519
所以通過追溯,我們知道了加final的目的,自然就知道為啥“方法引用”可以不受限制,因為你用它,它就能保證內外的數據一致性,但是lambda不行,那是因為lambda是可以進行擴展的,通俗來講就是可以寫很多行代碼,所以無法提前預知引入的外部變量會發生什么事情,所以lambda是要求必須是final修飾的。