第一部分
方法引用,又稱雙冒號(::),是簡化的lambda表達式,主要使用形式包括四種:
方法引用的形式(Kinds of Method References)
類型 | Kind | Example |
指向靜態方法的引用 | Reference to a static method | ContainingClass::staticMethodName |
指向特定對象實例方法的引用 | Reference to an instance method of a particular object | containingObject::instanceMethodName |
指向特定類型任意對象實例方法的引用 | Reference to an instance method of an arbitrary object of a particular type | ContainingType::methodName |
指向構造函數的引用 | Reference to a constructor | ClassName::new |
考慮以下例子:
Person類
1 import java.time.LocalDate; 2 3 public class Person { 4 5 public enum Sex { 6 MALE, FEMALE 7 } 8 9 String name; 10 LocalDate birthday; 11 Sex gender; 12 String emailAddress; 13 14 public Person(String name, LocalDate birthday, Sex gender) { 15 this.name = name; 16 this.birthday = birthday; 17 this.gender = gender; 18 } 19 20 public int getAge() { 21 int age = LocalDate.now().minusYears(birthday.getYear()).minusDays(birthday.getDayOfYear()).getYear(); 22 return age; 23 } 24 25 public LocalDate getBirthday() { 26 return birthday; 27 } 28 29 public static int compareByAge(Person a, Person b) { 30 return a.birthday.compareTo(b.birthday); 31 } 32 }
如果對Person數組排序,可通過下面這種方式,先聲明一個類,然后實例化。
①實例化類的寫法:
1 List<Person> roster = Arrays.asList(); 2 3 Person[] rosterAsArray = roster.toArray(new Person[roster.size()]); 4 5 class PersonAgeComparator implements Comparator<Person> { 6 public int compare(Person a, Person b) { 7 return a.getBirthday().compareTo(b.getBirthday()); 8 } 9 } 10 11 Arrays.sort(rosterAsArray, new PersonAgeComparator());
靜態方法sort的方法簽名:
1 static <T> void sort(T[] a, Comparator<? super T> c)
Comparator是一個函數式接口
1 @FunctionalInterface 2 public interface Comparator<T> { 3 4 int compare(T o1, T o2); 5 }
②可以通過lambda表達式,替換創建一個類:
返回值為compare的方法體
1 Arrays.sort(rosterAsArray, 2 (Person a, Person b) -> { 3 return a.getBirthday().compareTo(b.getBirthday()); 4 } 5 );
③單獨的表達式,可以去掉return和大括號,寫法為:
1 Arrays.sort(rosterAsArray, (Person a, Person b) -> a.getBirthday().compareTo(b.getBirthday()));
④參數類型可以自動推斷,可以去掉參數類型:
1 Arrays.sort(rosterAsArray, (a, b) -> a.getBirthday().compareTo(b.getBirthday()));
⑤方法引用可以使代碼進一步簡潔:
1 Arrays.sort(rosterAsArray, Person::compareByAge);
下面示例演示4種類型
1)指向靜態方法的引用
1 Arrays.sort(rosterAsArray, Person::compareByAge);
2)指向類實例方法的引用
1 String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" }; 2 Arrays.sort(stringArray, String::compareToIgnoreCase);
3)指向特定類型任意對象實例方法的引用
1 class ComparisonProvider { 2 public int compareByName(Person a, Person b) { 3 return a.getName().compareTo(b.getName()); 4 } 5 6 public int compareByAge(Person a, Person b) { 7 return a.getBirthday().compareTo(b.getBirthday()); 8 } 9 } 10 ComparisonProvider myComparisonProvider = new ComparisonProvider(); 11 Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
4)指向構造函數的引用
有如下方法,將一個集合轉為另外一個集合
1 public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>> 2 DEST transferElements( 3 SOURCE sourceCollection, 4 Supplier<DEST> collectionFactory) { 5 6 DEST result = collectionFactory.get(); 7 for (T t : sourceCollection) { 8 result.add(t); 9 } 10 return result; 11 }
轉換如下:
1 Set<Person> rosterSetLambda = transferElements(roster, () -> { return new HashSet<>(); });
簡潔寫法如下:
1 Set<Person> rosterSet = transferElements(roster, HashSet::new);
上述代碼隱藏了泛型:
1 Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
以上參考自:https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
- - - - - - - - - - - - - - - - - - - - 分割線 - - - - - - - - - - - - - - - - - - - -
第二部分
下面結合方法引用對應的(內置)函數式接口,通過代碼示例,詳細解說。
雙冒號(::)和 箭頭函數(->)一並展示如下:
如:HashMap::new 等同於 ( ) -> new HashMap()
熟悉方法引用對應的函數式接口很重要,因為它是方法的參數。熟悉了它,可以結合Java高級特性(泛型、反射等)寫出抽象可復用的函數式代碼。
1 import java.util.ArrayList; 2 import java.util.Arrays; 3 import java.util.LinkedList; 4 import java.util.List; 5 import java.util.function.BiConsumer; 6 import java.util.function.BiFunction; 7 import java.util.function.Consumer; 8 import java.util.function.Function; 9 import java.util.function.Supplier; 10 import java.util.stream.Collectors; 11 12 public class Test { 13 14 // 實例對象引用實例方法 15 Supplier<String> supplier1 = "lowerCase"::toUpperCase; 16 Supplier<String> supplier1_1 = () -> "lowerCase".toUpperCase(); 17 18 // 類引用(無參)構造函數 19 Supplier<String> supplier2 = String::new; 20 Supplier<String> supplier2_1 = () -> new String(); 21 22 // 類引用(有參)構造函數 23 Function<String, String> function1 = String::new; 24 Function<String, String> function1_1 = (String str) -> new String(str); 25 26 // 類引用實例方法,入參為傳入實例對象,入參、出參同類型 27 Function<String, String> function2 = String::toUpperCase; 28 Function<String, String> function2_1 = (String str) -> str.toUpperCase(); 29 30 // Predicate<T>可理解為特殊的Function<T, Boolean> 31 32 Person person = new Person(); 33 // 須為無參靜態方法 34 Supplier<Boolean> supplierBln = Person::isTest; 35 Supplier<Boolean> supplierBln_1 = () -> Person.isTest(); 36 37 // 實例對象調用實例方法 38 Supplier<String> supplierStr = person::getName; 39 Supplier<String> supplierStr_1 = () -> person.getName(); 40 41 // 無參構造函數 42 Supplier<Person> supplierPerson = Person::new; 43 Supplier<Person> supplierPerson_1 = () -> new Person(); 44 45 // 有參構造函數 46 BiFunction<String, String, Person> biFunction = Person::new; 47 BiFunction<String, String, Person> biFunction_1 = (name, gender) -> new Person(name, gender); 48 49 // 類名調用set方法,特定場景下,可取代反射 50 BiConsumer<Person, String> biConsumer = Person::setName; 51 52 // 類名調用實例方法,入參為傳入實例對象 53 Function<Person, Person> functionP = Person::toOpposite; 54 Function<Person, Person> functionP_1 = person -> person.toOpposite(); 55 56 Consumer<String> consumer = System.out::println; 57 Consumer<String> consumer_1 = (String str) -> System.out.println(str);; 58 59 public static void main(String[] args) { 60 List<String> list = Arrays.asList("1", "2", "3"); 61 boolean bl = list.stream().anyMatch("1"::equals); 62 List<String> retval = list.stream().collect(Collectors.toCollection(LinkedList::new)); 63 64 List<Person> persons = Arrays.asList(new Person(10, "Jack", "M")); 65 Person person = new Person(20, "Lily", "F"); 66 persons.stream().filter(Person::isMale).filter(person::isUnder).collect(Collectors.toCollection(ArrayList::new)); 67 } 68 }
Person類代碼如下:
1 public class Person { 2 int age; 3 String name; 4 String gender; 5 6 public Person() { 7 } 8 9 public Person(String name) { 10 this.name = name; 11 } 12 13 public Person(String name, String gender) { 14 this.name = name; 15 this.gender = gender; 16 } 17 18 public Person(int age, String name, String gender) { 19 this.age = age; 20 this.name = name; 21 this.gender = gender; 22 } 23 24 public String getName() { 25 return name; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 32 public Person toOpposite() { 33 if (gender.charAt(0) == 'M') 34 gender = "F"; 35 else 36 gender = "M"; 37 return this; 38 } 39 40 public static boolean isTest() { 41 return true; 42 } 43 44 public boolean isUnder(Person person) { 45 return person.age > this.age; 46 } 47 48 public boolean isMale() { 49 return gender.equals("M"); 50 } 51 }
- - - - - - - - - - - - - - - - - - - - 分割線 - - - - - - - - - - - - - - - - - - - -
第三部分
擴展至自定義的函數式接口
下面結合Elasticsearch,進一步實例講解
指向類靜態方法的引用
NodesUsageResponse節點使用響應,50行使用了方法引用
泛型方法,入參為函數Reader<V>
函數式接口Reader<V>,定義了方法reader(StreamInput in):
reader.read(this)實際調用函數是
第三種類型
指向特定類型任意對象實例方法的引用(Reference to an Instance Method of an Arbitrary Object of a Particular Type)
TransportService傳輸服務,構造函數164行使用了方法引用
成員變量asyncSender和interceptor
作用為請求發送攔截器接口TrsnsportInterceptor,調用的為49行,返回AsyncSender函數式接口,57行為函數式接口定義。
TransportService構造函數,使用了類實例方法sendRequestInternal
方法sendRequestInternal的方法參數列表、返回值類型和接口TransportInterceptor中的函數是接口AsyncSender一致。
第四種類型
指向構造函數的應用
構造函數,以67行為例
泛型方法 StreamInput # readOptionalWriteable
方法引用DiscoveryNode::new,實際調用構造函數如下