Java8新特性之二:方法引用
上一節介紹了Java8新特性中的Lambda表達式,本小節繼續講解Java8的新特性之二:方法引用。方法引用其實也離不開Lambda表達式。
1、方法引用的使用場景
我們用Lambda表達式來實現匿名方法。但有些情況下,我們用Lambda表達式僅僅是調用一些已經存在的方法,除了調用動作外,沒有其他任何多余的動作,在這種情況下,我們傾向於通過方法名來調用它,而Lambda表達式可以幫助我們實現這一要求,它使得Lambda在調用那些已經擁有方法名的方法的代碼更簡潔、更容易理解。方法引用可以理解為Lambda表達式的另外一種表現形式。
2、方法引用的分類
語法 | 對應的Lambda表達式 | |
---|---|---|
靜態方法引用 | 類名::staticMethod | (args) -> 類名.staticMethod(args) |
實例方法引用 | inst::instMethod | (args) -> inst.instMethod(args) |
對象方法引用 | 類名::instMethod | (inst,args) -> 類名.instMethod(args) |
構建方法引用 | 類名::new | (args) -> new 類名(args) |
3、方法引用舉例
3.1 靜態方法引用
有一個Person類,如下所示:
1@Data 2 public class Person { 3 4 private String name; 5 6 private Integer age; 7 8 public static int compareByAge(Person a, Person b) { 9 return a.age.compareTo(b.age); 10 } 11 }
現假設,一個部門有30人,把他們存放在一個數組中,並按年齡排序,通常我們可以自己寫一個比較器,代碼如下:
1 Person[] rosterAsArray = newPerson[30]; 2 // 添加數組元素省略 3 4 class PersonAgeComparator implements Comparator<Person> { 5 public int compare(Person a, Person b) { 6 return a.getBirthday().compareTo(b.getBirthday()); 7 } 8 } 9 10 Arrays.sort(rosterAsArray, new PersonAgeComparator());
Arrays.sort的聲明為:public static <T> void sort(T[] a, Comparator<? super T> c),比較器參數Comparator為一個函數式接口,利用上一節Lambda表達式所學知識,可以改寫為以下代碼:
1 Person[] rosterAsArray = new Person[30]; 2 // 添加數組元素省略 3 4 Arrays.sort(rosterAsArray, (a,b) -> a.getAge().compareTo(b.getAge()));
然而,你會發現,Perdon類中已經有了一個靜態方法的比較器:compareByAge,因此,我們改用Person類已經提供的比較器:
1 Person[] rosterAsArray = new Person[30]; 2 // 添加數組元素省略 3 4 Arrays.sort(rosterAsArray, (a,b) -> Person.compareByAge(a,b));
以上代碼,因為Lambda表達式調用了一個已經存在的靜態方法,根據我們第2節表格中的語法,上面的代碼可以最終改寫成靜態方法引用:
1 Person[] rosterAsArray = new Person[30]; 2 // 添加數組元素省略 3 4 Arrays.sort(rosterAsArray, Person::compareByAge);
下面這個例子更簡單:
1 public class Test { 2 public static void main(String[] args) { 3 List<Integer> list = Arrays.asList(82,22,34,50,9); 4 list.sort(Integer::compare); 5 System.out.println(list); 6 } 7 }
對一個Integer列表進行排序,因為Integer中已經存在靜態的比較方法compare(),因此可以直接用靜態方法引用的方式來調用 ,運行結果為:
[9, 22, 34, 50, 82]
3.2 實例方法引用
實例方法引用,顧名思義就是調用已經存在的實例的方法,與靜態方法引用不同的是類要先實例化,靜態方法引用類無需實例化,直接用類名去調用。
1 @Data 2 class User { 3 4 private String name; 5 private Integer age; 6 7 public User(String name, Integer age) { 8 this.name = name; 9 this.age = age; 10 } 11 } 12 13 public class TestInstanceReference { 14 15 public static void main(String[] args) { 16 17 TestInstanceReference test = new TestInstanceReference(); 18 User user = new User("歐陽峰",32); 19 Supplier<String> supplier = () -> user.getName(); 20 System.out.println("Lambda表達式輸出結果:" + supplier.get()); 21 22 Supplier<String> supplier2 = user::getName; 23 System.out.println("實例方法引用輸出結果:" + supplier2.get()); 24 } 25 }
輸出結果:
Lambda表達式輸出結果:歐陽峰
實例方法引用輸出結果:歐陽峰
3.3 對象方法引用
若Lambda參數列表中的第一個參數是實例方法的參數調用者,而第二個參數是實例方法的參數時,可以使用對象方法引用。
String的equals()方法:
1 public boolean equals(Object anObject) { 2 if (this == anObject) { 3 return true; 4 } 5 if (anObject instanceof String) { 6 String anotherString = (String)anObject; 7 int n = value.length; 8 if (n == anotherString.value.length) { 9 char v1[] = value; 10 char v2[] = anotherString.value; 11 int i = 0; 12 while (n-- != 0) { 13 if (v1[i] != v2[i]) 14 return false; 15 i++; 16 } 17 return true; 18 } 19 } 20 return false; 21 }
1 public static void main(String[] args) { 2 3 BiPredicate<String,String> bp = (x, y) -> x.equals(y); 4 BiPredicate<String,String> bp1 = String::equals; 5 6 boolean test = bp1.test("xy", "xx"); 7 System.out.println(test); 8 }
BiPredicate的test()方法接受兩個參數,x和y,具體實現為x.equals(y),滿足Lambda參數列表中的第一個參數是實例方法的參數調用者,而第二個參數是實例方法的參數,因此可以使用對象方法引用。
3.4 構造方法引用
註意:需要調用的構造器的參數列表要與函數式接口中抽象方法的參數列表保持一致。
如:要獲取一個空的User列表:
1 Supplier<List<User>> userSupplier = () -> new ArrayList<>(); 2 List<User> user = userSupplier.get(); 3 4 Supplier<List<User>> userSupplier2 = ArrayList<User>::new; // 構造方法引用寫法 5 List<User> user2 = userSupplier.get();
至此,方法引用講完了,下一章節將講解Stream API。
Java8新特性之二:方法引用