Java中的函數語言程式設計(四)方法引用method reference
阿新 • • 發佈:2021-10-23
寫在前面
我們已經知道,lambda表示式是一個匿名函式,可以用lambda表示式來實現一個函式式介面。 很自然的,我們會想到類的方法也是函式,本質上和lambda表示式是一樣的,那是否也可以用類的方法來實現一個函式式介面呢?答案是可以的。我們稱之為方法引用(methodreference)。 本文的示例程式碼可從gitee上獲取:https://gitee.com/cnmemset/javafp方法引用
一個典型例子,向一個Map中寫入單詞以及它的長度:public static void simpleMethodReference() { Map<String, Integer> wordMap = new上述程式碼中,String::length就是方法引用,它用:: 來分割類名或物件與方法名,:: 左側是類名或物件,:: 右側是方法名。 一般來說,方法引用有4種情況: 1.object::instanceMethod ——物件+ 例項方法 2.Class::staticMethod ——類名+ 靜態方法 3.Class::instanceMethod——類名+ 例項方法 4.Class::new ——類名+ 關鍵字new,這種情況又稱為構造器引用(constructor reference)HashMap<>(); // 等同於 wordMap.computeIfAbsent("hello", s -> s.length()); wordMap.computeIfAbsent("hello", String::length); // 輸出為 {hello=5} System.out.println(wordMap); }
1. object::instanceMethod
public static上述程式碼的輸出是: {him=5, you=-12}void objectInstanceMethodReference() { String me = "me"; // wordMap 的 key 是給定的單詞,value是不區分大小寫,與單詞 "me" 比較後得出的值 Map<String, Integer> wordMap = new HashMap<>(); // me::compareToIgnoreCase 等價於 s -> me.compareToIgnoreCase(s) wordMap.computeIfAbsent("him", me::compareToIgnoreCase); wordMap.computeIfAbsent("you", s -> me.compareToIgnoreCase(s)); System.out.println(wordMap); }
2. Class::staticMethod
Class::staticMethod,:: 左側是一個類,:: 右側是靜態方法名。 它等價於提供了staticMethod方法的引數列表的lambda表示式。 形象來說,假設靜態方法 staticMethod 的引數列表為 (x, y),那麼 Class::staticMethod 等價於 (x, y, z) -> Class.staticMethod(x, y) 。 例如: System.out::println 等價於x -> System.out.print(x) 示例程式碼:public static void classStaticMethodReference() { List<String> list = Arrays.asList("Guangdong", "Zhejiang", "Jiangsu"); // System.out::println 等價於 s -> System.out.println(s) list.forEach(System.out::println); }上述程式碼輸出為: Guangdong Zhejiang Jiangsu
3. Class::instanceMethod
對於Class::instanceMethod,:: 左側是一個類,:: 右側是例項方法名。 假設instanceMethod的引數列表是(x, y),那麼Class::instanceMethod等價於lambda表示式(obj, x, y) -> obj.instanceMethod(x, y),其中obj是Class的物件例項。 例如: String::length 等價於s-> s.length() String::compareToIgnoreCase等價於(s1, s2) -> s1.compareToIgnoreCase(s2) 示例程式碼:public static void classInstanceMethodReference() { Map<String, Integer> wordMap = new HashMap<>(); Integer wordLen = wordMap.computeIfAbsent("hello", String::length); System.out.println(wordMap); }上述程式碼輸出為: {hello=5}
4. Class::new
對於Class::new,new的含義是指Class的建構函式,所以又稱為構造器引用(constructor reference)。 假設Class的建構函式有兩個,它們的引數列表分別是(x)和(x, y),那麼Class::new可能等價於x-> new Class(x),也有可能等價於(x, y) -> new Class(x, y),具體是哪個,編譯器會在編譯階段通過上下文推斷出來。 例如: BigDecimal::new ,根據上下文,可能等價於(String s) -> new BigDecimal(s) 特別的,陣列型別也可以使用構造器引用。陣列型別只有一個構造引數,表示陣列的長度: String[]::new 等價於x -> new String[x] 示例程式碼:public static void ctorMethodReference() { List<String> list = Arrays.asList("1.1", "2.2", "3.3"); // BigDecimal::new 根據上下文推斷,等價於 s -> new BigDecimal(s) Stream<BigDecimal> stream = list.stream().map(BigDecimal::new); List<BigDecimal> decimalList = stream.collect(Collectors.toList()); System.out.println(decimalList); // 構建一個新的 Stream ,之前的 Stream 已經被關閉了 Stream<BigDecimal> stream1 = list.stream().map(BigDecimal::new); // BigDecimal[]::new ,陣列的構造器引用,等價於 x -> new BigDecimal[x] BigDecimal[] decimalArray = stream1.toArray(BigDecimal[]::new); for (BigDecimal d : decimalArray) { System.out.println(d); } }上述程式碼的輸出為: [1.1, 2.2, 3.3] 1.1 2.2 3.3
5. this::instanceMethod和super::instanceMethod
對於this::instanceMethod,很容易理解,相當於把this關鍵字看做是當前類的例項物件即可。 例如: this::equals 等價於x -> this.equals(x) 對於super::instanceMethod,會相對複雜一些,相當於在this物件上,呼叫的指定方法父類版本。 示例程式碼:public class SuperMethodReferenceExample { public static void main(String[] args) { ThreadWaiter waiter = new ThreadWaiter(); waiter.run(); } public static class Waiter { public void sayHi() { System.out.println("Hello, man!"); } } public static class ThreadWaiter extends Waiter { @Override public void sayHi() { System.out.println("Hello, thread!"); } public void run() { // 指定呼叫父類 Waiter 的 sayHi 方法 Thread t = new Thread(super::sayHi); t.start(); } } }
上述程式碼的輸出為:
Hello, man!