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 4private String name; 5 6private Integer age; 7 8public static int compareByAge(Person a, Person b) { 9return a.age.compareTo(b.age); 10} 11 }
現假設,一個部門有30人,把他們存放在一個數組中,並按年齡排序,通常我們可以自己寫一個比較器,程式碼如下:
1 Person[] rosterAsArray = new Person[30]; 2 // 新增陣列元素省略 3 4 class PersonAgeComparator implements Comparator<Person> { 5public int compare(Person a, Person b) { 6return 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 { 2public static void main(String[] args) { 3List<Integer> list = Arrays.asList(82,22,34,50,9); 4list.sort(Integer::compare); 5System.out.println(list); 6} 7 }
對一個Integer列表進行排序,因為Integer中已經存在靜態的比較方法compare(),因此可以直接用靜態方法引用的方式來呼叫 ,執行結果為:
[9, 22, 34, 50, 82]
3.2 例項方法引用
例項方法引用,顧名思義就是呼叫已經存在的例項的方法,與靜態方法引用不同的是類要先例項化,靜態方法引用類無需例項化,直接用類名去呼叫。
1 @Data 2 class User { 3 4private String name; 5private Integer age; 6 7public User(String name, Integer age) { 8this.name = name; 9this.age = age; 10} 11 } 12 13 public class TestInstanceReference { 14 15public static void main(String[] args) { 16 17TestInstanceReference test = new TestInstanceReference(); 18User user = new User("歐陽峰",32); 19Supplier<String> supplier = () -> user.getName(); 20System.out.println("Lambda表示式輸出結果:" + supplier.get()); 21 22Supplier<String> supplier2 = user::getName; 23System.out.println("例項方法引用輸出結果:" + supplier2.get()); 24} 25 }
輸出結果:
Lambda表示式輸出結果:歐陽峰 例項方法引用輸出結果:歐陽峰
3.3 物件方法引用
若Lambda引數列表中的第一個引數是例項方法的引數呼叫者,而第二個引數是例項方法的引數時,可以使用物件方法引用。
String的equals()方法:
1 public boolean equals(Object anObject) { 2if (this == anObject) { 3return true; 4} 5if (anObject instanceof String) { 6String anotherString = (String)anObject; 7int n = value.length; 8if (n == anotherString.value.length) { 9char v1[] = value; 10char v2[] = anotherString.value; 11int i = 0; 12while (n-- != 0) { 13if (v1[i] != v2[i]) 14return false; 15i++; 16} 17return true; 18} 19} 20return false; 21}
1 public static void main(String[] args) { 2 3BiPredicate<String,String> bp = (x, y) -> x.equals(y); 4BiPredicate<String,String> bp1 = String::equals; 5 6boolean test = bp1.test("xy", "xx"); 7System.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。