函數式編程,方法引用,Java雙冒號(::)示例詳解


第一部分

方法引用,又稱雙冒號(::),是簡化的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( Connection connection, String action, TransportRequest request, TransportRequestOptions options, TransportResponseHandler<T> handler)

方法sendRequestInternal的方法參數列表、返回值類型和接口TransportInterceptor中的函數是接口AsyncSender一致。

第四種類型

指向構造函數的應用

構造函數,以67行為例

泛型方法 StreamInput # readOptionalWriteable

方法引用DiscoveryNode::new,實際調用構造函數如下

 


免責聲明!

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



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