☕️【Java技術之旅】帶你看透Lambda表達式的本質

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"📚 Lambda出現之前","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📚 Anonymous匿名類","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 線程去完成任務時,會通過Runnable 接口來定義任務內容,並使用Thread 類來啓動該線程。 創建Runnable接口的匿名內部類對象來指定線程要執行的任務內容,再將其交給一個線程來啓動!傳統寫法,代碼如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Demo01LambdaIntro {\n public static void main(String[] args) {\n new Thread(new Runnable() {\n @Override\n public void run() {\n System.out.println(\"新線程任務執行!\");\n }\n }).start();\n }\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 分析","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 對於Runnable 的匿名內部類用法,可以分析出幾點內容:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"Thread 類需要Runnable 接口作爲參數,其中的抽象run方法是用來指定線程任務內容的核心;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"爲了指定run的方法體,不得不需要Runnable 接口的實現類;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"爲了省去定義一個Runnable 實現類的麻煩,不得不使用匿名內部類;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"必須覆蓋重寫抽象run 方法,方法名稱、方法參數、方法返回值不得不再寫一遍,且不能寫錯;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"而實際上,似乎只有方法體纔是關鍵所在;","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📚 Lambda表達式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 藉助Java 8的全新語法,上述Runnable接口的匿名內部類寫法可以通過更簡單的Lambda表達式達到相同的效果Lambda表達式寫法,代碼如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Demo01LambdaIntro {\n public static void main(String[] args) {\n new Thread(() -> System.out.println(\"新線程任務執行!\")).start(); // 啓動線程\n }\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"📚 優點","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"簡化匿名內部類的使用,語法更加簡單;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📚 Lambda標準格式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"Lambda的標準格式格式由3個部分組成:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":9}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"(參數類型 參數名稱) -> {","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":9}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"\t代碼體;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":9}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 格式說明","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"(參數類型 參數名稱):參數列表;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"{代碼體;}:方法體;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"-> :箭頭,分隔參數列表和方法體;","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 無參數無返回值Lambda","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Test {\n \n public static void main(String[] args) throws Exception {\n \t\tgoSwimming(new Swimmable() {\n @Override\n public void swimming() {\n System.out.println(\"匿名內部類游泳\");\n }\n });\n goSwimming(()->{ System.out.println(\"Lambda表達式游泳\"); });\n }\n public static void goSwimming(Swimmable swimmable) {\n swimmable.swimming();\n }\n}\ninterface Swimmable {\n public abstract void swimming();\n}\n\n\n//控制檯打印\n匿名內部類游泳\nLambda表達式游泳","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 有參數有返回值Lambda","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Test {\n public static void main(String[] args) throws Exception {\n goSwimming(new Swimmable() {\n @Override\n public String swimming(String str) {\n return str.toUpperCase();\n }\n });\n goSwimming((str)->{ return str.toLowerCase(); });\n }\n public static void goSwimming(Swimmable swimmable) {\n String str = \"Hello World\";\n str = swimmable.swimming(str);\n System.out.println(str);\n }\n}\ninterface Swimmable {\n public abstract String swimming(String str);\n}\n//控制檯打印\nHELLO WORLD\nhello world","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"以後我們調用方法時,看到參數是接口並且該接口只有一個抽象方法就可以考慮使用","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"Lambda","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"表達式,","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"Lambda","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"表達式相當於是對接口中抽象方法的重寫;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📚 Lambda實現原理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 還是看前面Test類的例子,我們可以看到匿名內部類會在編譯後產生一個類:Test$1.class","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d4/d42762674220c2d86bbbf85085e1f995.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 反編譯Test$1.class代碼爲","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"final class Test$1 implements Swimmable{\n public String swimming(String str){\n return str.toUpperCase();\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 我們再來看看Lambda的效果,修改Test.java去除匿名內部類的調用:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Test {\n \n public static void main(String[] args) throws Exception {\n goSwimming((str)->{ return str.toLowerCase(); });\n }\n public static void goSwimming(Swimmable swimmable) {\n String str = \"Hello World\";\n str = swimmable.swimming(str);\n System.out.println(str);\n }\n}\ninterface Swimmable {\n public abstract String swimming(String str);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 運行程序,控制檯可以得到預期的結果,但是並沒有出現一個新的類,","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"也就是說Lambda並沒有在編譯的時候產生一個新的類","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":",進行反編譯報錯。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/24/24f41db02d49dc88864b359b0f25a27d.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"我們使用JDK自帶的一個工具: javap ,對字節碼進行反彙編,查看字節碼指令:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"javap -c -p 文件名.class\n-c:表示對代碼進行反彙編\n-p:顯示所有類和成員","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"PS F:\\CloneTest\\out\\production\\CloneTest> javap -c -p Test.class\nCompiled from \"Test.java\"\npublic class Test {\n public Test();\n Code:\n 0: aload_0\n 1: invokespecial #1 // Method java/lang/Object.\"\":()V\n 4: return\n\n public static void main(java.lang.String[]) throws java.lang.Exception;\n Code:\n 0: invokedynamic #2, 0 // InvokeDynamic #0:swimming:()LSwimmable;\n 5: invokestatic #3 // Method goSwimming:(LSwimmable;)V\n 8: return\n\n public static void goSwimming(Swimmable);\n Code:\n 0: ldc #4 // String Hello World\n 2: astore_1\n 3: aload_0\n 4: aload_1\n 5: invokeinterface #5, 2 // InterfaceMethod Swimmable.swimming:(Ljava/lang/String;)Ljava/lang/String;\n 10: astore_1\n 11: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;\n 14: aload_1\n 15: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V\n 18: return\n \n private static java.lang.String lambda$main$0(java.lang.String);\n Code:\n 0: aload_0\n 1: invokevirtual #8 // Method java/lang/String.toLowerCase:()Ljava/lang/String;\n 4: areturn\n}\nPS F:\\CloneTest\\out\\production\\CloneTest>","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":" 看到在類中多出了一個私有的靜態方法lambda$main$0 。這個方法裏面放的是什麼內容呢?通過斷點調試來看看:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/43/43425982b562b50e3770cfce7167505b.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"可以確認lambda$main$0 裏面放的就是Lambda中的內容;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" ","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"lambda$main0","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"的命名 : 以 lambda開頭 , 因爲是在 main()函數裏使用了lambda表達式 , 所以帶有0的命名:以lambda開頭,因爲是在main()函數裏使用了lambda表達式,","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"所以帶有0的命名:以","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}},{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"lambda","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"開頭,因爲是在","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}},{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"main","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"()函數裏使用了","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}},{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"lambda","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"表達式,所以帶有main表示,因爲是第一個,所以$0","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/65/65046f5813710fc82b69b9a097f57fba.png","alt":"在這裏插入圖片描述","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"添加JVM參數:","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"-Djdk.internal.lambda.dumpProxyClasses","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#000000","name":"black"}},{"type":"strong","attrs":{}}],"text":" 進行輸出相關的Lambda表達式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"加上這個參數後,運行時會將生成的內部類class碼輸出到一個文","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.lang.invoke.LambdaForm.Hidden;\n// 形成一個匿名內部類,實現接口,重寫抽象方法\n// $FF: synthetic class\nfinal class Test$$Lambda$1 implements Swimmable {\n private Test$$Lambda$1() {}\n @Hidden\n public String swimming(String var1) {\n \t\t//接口的重寫方法中會調用新生成的方法\n return Test.lambda$main$0(var1);\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 可以看到這個匿名內部類實現了Swimmable接口,並且重寫了swimming 方法, swimming方法調用return Test.lambda$main$0(var1),也就是調用Lambda中的內容。最後可以將Lambda理解爲:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Test {\n public static void main(String[] args) throws Exception {\n goSwimming(new Swimmable() {\n @Override\n public String swimming(String str) {\n return lambda$main$0(str); \n //對應前面debug到這,是執行lambda$main$0方法\n }\n });\n }\n\t//新增一個方法,這個方法的方法體就是Lambda表達式中的代碼\n private static String lambda$main$0(String str) {\n return str.toLowerCase();\n }\n \n public static void goSwimming(Swimmable swimmable) {\n String str = \"Hello World\";\n str = swimmable.swimming(str);\n System.out.println(str);\n }\n}\ninterface Swimmable {\n public abstract String swimming(String str);\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 匿名類和Lambda的區別","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"匿名內部類在編譯的時候會一個class文件,Lambda在程序運行的時候形成一個類:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"在類中新增一個靜態方法,這個方法的方法體就是Lambda表達式中的代碼;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"還會形成一個匿名內部類,實現接口,重寫抽象方法;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"在接口的重寫方法中會調用新生成的方法;","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📚 Lambda省略格式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"在Lambda標準格式的基礎上,使用省略寫法的規則爲:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"小括號內參數的類型可以省略;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"如果小括號內有且僅有一個參數,則小括號可以省略;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"如果大括號內有且僅有一個語句,可以同時省略大括號、return關鍵字及語句分號;","attrs":{}}]}]}],"attrs":{}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"(int a) -> {return new Person();}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📚 Lambda的前提條件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"Lambda的語法非常簡潔,但是Lambda表達式不是隨便使用的,使用時有幾個條件要特別注意:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"方法的參數或局部變量類型必須爲接口才能使用Lambda;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong","attrs":{}}],"text":"接口中有且僅有一個抽象方法;(屬於函數接口)","attrs":{}}]}]}],"attrs":{}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Test {\n public static void main(String[] args) throws Exception {\n //方法的參數是接口:Lambda表達式,省略參數類型以及{}、return、;\n goSwimming(str -> str.toUpperCase());\n //局部變量類型爲接口:匿名內部類形式\n Swimmable swimmable = new Swimmable() {\n @Override\n public String swimming(String str) {\n return null;\n }\n };\n //局部變量類型爲接口:Lambda表達式\n Swimmable swimmable2 = str -> str.toUpperCase();\n }\n public static void goSwimming(Swimmable swimmable) {\n String str = \"Hello World\";\n str = swimmable.swimming(str);\n System.out.println(str);\n }\n}\n//定義一個接口,有且僅有一個抽象方法\ninterface Swimmable {\n public abstract String swimming(String str);\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📚 函數式接口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"函數式接口在Java中是指:有且僅有一個抽象方法的接口","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}}],"text":";","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"函數式接口,即適用於函數式編程場景的接口。Java中的","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"函數式編程體現就是Lambda","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":",所以函數式接口就是可以適","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"用於Lambda使用的接口。","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"只有確保接口中有且僅有一個抽象方法,Java中的Lambda才能順利地進行推導","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":";","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 FunctionalInterface註解","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 與@Override 註解的作用類似,Java 8中專門爲函數式接口引入了一個新的註解: @FunctionalInterface 。該註解可用於一個接口的定義上:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@FunctionalInterface\npublic interface Operator {\n\tvoid myMethod();\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 一旦使用該註解來定義接口,編譯器將會強制檢查該接口是否確實有且僅有一個抽象方法,否則將會報錯。不過,即使不使用該註解,只要滿足函數式接口的定義,這仍然是一個函數式接口,使用起來都一樣;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 常用內置函數式接口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 我們知道使用Lambda表達式的前提是需要有函數式接口。","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"而Lambda使用時不關心接口名,抽象方法名,只關心抽象方法的參數列表和返回值類型","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"。因此爲了讓我們使用Lambda方便,JDK提供了大量常用的函數式接口;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 它們主要在","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"java.util.function","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"包中,下面是最常用的幾個接口:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 ","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000000","name":"black"}}],"text":"Supplier接口","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@FunctionalInterface\npublic interface Supplier {\n\tpublic abstract T get();\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" java.util.function.Supplier接口,意味着“供給” , 對應的Lambda表達式需要“對外提供”符合泛型類型的對象數據;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 供給型接口,通過Supplier接口中的get方法可以得到一個值,無參有返回的接口;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"使用場景演示:使用Lambda表達式返回數組元素最大值!","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.Arrays;\nimport java.util.function.Supplier;\n\npublic class Test {\n public static void main(String[] args) throws Exception {\n printMax(() -> {\n int[] arr = {10, 20, 100, 30, 40, 50};\n // 先排序,最後就是最大的\n Arrays.sort(arr);\n return arr[arr.length - 1]; // 最後就是最大的\n });\n }\n private static void printMax(Supplier supplier) {\n int max = supplier.get();\n System.out.println(\"max = \" + max);\n }\n}\n//控制檯打印:max = 100","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 Consumer接口:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@FunctionalInterface\npublic interface Consumer {\n\tpublic abstract void accept(T t);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":" java.util.function.Consumer","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"接口則正好相反,","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"它不是生產一個數據,而是消費一個數據","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":",其數據類型由泛型參數決定;使用場景演示:","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong","attrs":{}}],"text":"使用Lambda表達式將一個字符串轉成大寫和小寫的字符串","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"!","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Consumer;\npublic class Test {\n public static void main(String[] args) {\n // Lambda表達式\n test((String s) -> {\n System.out.println(s.toLowerCase());\n });\n }\n public static void test(Consumer consumer) {\n consumer.accept(\"HelloWorld\");\n }\n}\n//控制檯打印:helloworld","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"✒️ 默認方法:andThen","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" ","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"如果一個方法的參數和返回值全都是Consumer 類型,那麼就可以實現效果:消費一個數據的時候,首先做一個操作,然後再做一個操作,實現組合。而這個方法就是Consumer 接口中的default方法andThen;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":" 要想實現組合,需要兩個或多個Lambda表達式即可,而andThen 的語義正是“一步接一步”操作。例如兩個步驟組合的情況:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"default Consumer andThen(Consumer super T> after) {\n\t\tObjects.requireNonNull(after);\n\t\t\treturn (T t) -> { \n \taccept(t); \n after.accept(t); \n };\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"例如兩個步驟組合的情況:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Consumer;\n\npublic class Test {\n public static void main(String[] args) {\n // Lambda表達式\n test((String s) -> {\n System.out.println(s.toLowerCase());\n }, (String s) -> {\n System.out.println(s.toUpperCase());\n });\n // Lambda表達式簡寫\n /*test(s -> System.out.println(s.toLowerCase()), s ->\n System.out.println(s.toUpperCase()));*/\n }\n \n public static void test(Consumer c1, Consumer c2) {\n String str = \"Hello World\";\n //先執行c1(轉爲小寫)再執行c2(轉爲大寫)\n c1.andThen(c2).accept(str);\n //先執行c2(轉爲大寫)再執行c1(轉爲小寫)\n c2.andThen(c1).accept(str);\n }\n}\n//控制檯輸出:\nhello world\nHELLO WORLD\nHELLO WORLD\nhello world","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 Function接口:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@FunctionalInterface\npublic interface Function {\n\tpublic abstract R apply(T t);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" java.util.function.Function 接口用來根據一個類型的數據得到另一個類型的數據,前者稱爲前置條件,後者稱爲後置條件。有參數有返回值","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Function;\n\npublic class Test {\n\t\t//\n\t\tpublic static void main(String[] args) {\n // Lambda表達式\n test((String s) -> {\n return Integer.parseInt(s); // 10\n });\n }\n public static void test(Function function) {\n Integer in = function.apply(\"10\");\n System.out.println(\"in: \" + (in + 5));\n }\n}\n//控制檯輸出:\nin: 15","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"默認方法:andThen","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"Function 接口中有一個默認的 andThen 方法,用來進行組合操作。JDK源代碼如:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"default Function andThen(Function super R, ? extends V> after) {\n\t\tObjects.requireNonNull(after);\n\t\treturn (T t) -> after.apply(apply(t));\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"該方法同樣用於“先做什麼,再做什麼”的場景,和 Consumer 中的 andThen 差不多:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Function;\n\npublic class Test {\n \n public static void main(String[] args) {\n // Lambda表達式\n test((String s) -> {\n return Integer.parseInt(s);\n }, (Integer i) -> {\n return i * 10;\n });\n }\n public static void test(Function f1, Function f2) {\n // Integer in = f1.apply(\"66\"); // 將字符串解析成爲int數字\n // Integer in2 = f2.apply(in);// 將上一步的int數字乘以10\n Integer in3 = f1.andThen(f2).apply(\"66\");\n System.out.println(\"in3: \" + in3); // 660\n }\n}\n//控制檯輸出:in3: 660","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 第一個操作是將字符串解析成爲int數字,第二個操作是乘以10。兩個操作通過 andThen 按照前後順序組合到了一起;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 Predicate接口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"Predicate接口用於做判斷,返回boolean類型的值","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@FunctionalInterface\npublic interface Predicate {\n public abstract boolean test(T t);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 有時候需要對某種類型的數據進行判斷,從而得到一個boolean值結果。這時可以使用java.util.function.Predicate 接口;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 例如:使用Lambda判斷一個人名如果超過3個字就認爲是很長的名字","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Predicate;\n\npublic class Test {\n public static void main(String[] args) {\n test(s -> s.length() > 3, \"迪麗熱巴\");\n }\n private static void test(Predicate predicate, String str) {\n boolean veryLong = predicate.test(str);\n System.out.println(\"名字很長嗎:\" + veryLong);\n }\n\n}\n//控制檯輸出:名字很長嗎:true","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"Predicate擁有三個默認方法:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"默認方法:and,將兩個 Predicate 條件使用“與”邏輯連接起來實現“並且”的效果時,可以使用default方法 and 。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"默認方法:or,與 and 的“與”類似,默認方法 or 實現邏輯關係中的“或”。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"默認方法:negate,“與”、“或”已經瞭解了,剩下的“非”(取反)也會簡單。默認方法 negate 的JDK源代碼爲:","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"📚 方法引用","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Consumer;\n\npublic class Test {\n //傳統定義一個方法,直接調用即可\n public static void getMax(int[] arr) {\n int sum = 0;\n for (int n : arr) {\n sum += n;\n }\n System.out.println(sum);\n }\n public static void main(String[] args) {\n //Lambda表達式實現,需要定義一個方法,然後lambda裏面還要再實現一次。對比一下,lambda有點冗餘了\n printMax((int[] arr) -> {\n int sum = 0;\n for (int n : arr) {\n sum += n;\n }\n System.out.println(sum);\n });\n }\n private static void printMax(Consumer consumer) {\n int[] arr = {10, 20, 30, 40, 50};\n consumer.accept(arr);\n }\n}\n//控制檯輸出:150","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 如果我們在Lambda中所指定的功能,已經有其他方法存在相同方案,那是否還有必要再寫重複邏輯?可以直接“引用”過去就好了:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Consumer;\n\npublic class Test {\n public static void getMax(int[] arr) {\n int sum = 0;\n for (int n : arr) {\n sum += n;\n }\n System.out.println(sum);\n }\n public static void main(String[] args) {\n printMax(Test::getMax);\n }\n private static void printMax(Consumer consumer) {\n int[] arr = {10, 20, 30, 40, 50};\n consumer.accept(arr);\n }\n}\n//控制檯輸出:150","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":" 請注意其中的雙冒號 :: 寫法,這被稱爲“方法引用”,是一種新的語法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"方法引用的格式:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"符號表示 : ","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"::","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":";","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"符號說明 : 雙冒號爲方法引用運算符,而它所在的表達式被稱爲方法引用;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"如果Lambda所要實現的方案 , 已經有其他方法存在相同方案,那麼則可以使用方法引用;","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"常見引用方式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"方法引用在JDK 8中使用方式相當靈活,有以下幾種形式:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"instanceName::methodName 對象::方法名;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"ClassName::staticMethodName 類名::靜態方法;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"ClassName::methodName 類名::普通方法;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"ClassName::new 類名::new 調用的構造器;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"TypeName[]::new String[]::new 調用數組的構造器;","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"對象名::引用成員方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"如果一個類中已經存在了一個成員方法,則可以通過對象名引用成員方法,代碼爲","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.Date;\nimport java.util.function.Supplier;\n\npublic class Test {\n public static void main(String[] args) {\n Date now = new Date();\n //局部變量類型爲函數式接口時可以使用lambda\n Supplier supp = () -> {\n return now.getTime();\n };\n System.out.println(supp.get());\n Supplier supp2 = now::getTime;\n System.out.println(supp2.get());\n }\n}\n//控制檯輸出:\n1587694986055\n1587694986055","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"方法引用的注意事項:","attrs":{}}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"被引用的方法,參數要和接口中抽象方法的參數一樣;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"當接口抽象方法有返回值時,被引用的方法也必須有返回值;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"比如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"類名::引用靜態方法","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Supplier;\n\npublic class Test {\n public static void main(String[] args) {\n Supplier supp = () -> {\n return System.currentTimeMillis();\n };\n System.out.println(supp.get());\n Supplier supp2 = System::currentTimeMillis;\n System.out.println(supp2.get());\n }\n}\n//控制檯輸出:\n1587695223790\n1587695223809\n//public static native long currentTimeMillis();","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"由於在 java.lang.System 類中已經存在了靜態方法 currentTimeMillis ,所以當我們需要通過Lambda來調用該方法時,可以使用方法引用 ;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"類名::引用實例方法","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Function;\n\npublic class Test {\n public static void main(String[] args) {\n //Lambda表達式\n Function f1 = (s) -> {\n return s.length();\n };\n System.out.println(f1.apply(\"abc\"));\n //方法引用\n Function f2 = String::length;\n System.out.println(f2.apply(\"abc\"));\n }\n}\n//控制檯打印:\n3\n3\n//public int length() { return value.length;}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"類名::new引用構造器","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Person {\n private String name;\n private Integer age;\n\n public Person(String name, Integer age) {\n this.name = name;\n this.age = age;\n }\n\n public Person() {\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.BiFunction;\nimport java.util.function.Supplier;\n\npublic class Test {\n public static void main(String[] args) {\n //Lambda表達式\n Supplier sup = () -> {\n return new Person();\n };\n System.out.println(sup.get());\n //方法引用\n Supplier sup2 = Person::new;\n System.out.println(sup2.get());\n //帶入參的方法引用,BiFunction(String, Integer爲方法入參,Person爲出參)\n BiFunction fun2 = Person::new;\n System.out.println(fun2.apply(\"張三\", 18));\n }\n}\n//控制檯打印:\nPerson@3d075dc0\nPerson@7cca494b\nPerson@58372a00","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"數組::new 引用數組構造器","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.function.Function;\n\npublic class Test {\n public static void main(String[] args) {\n //Lambda表達式\n Function fun = (len) -> {\n return new String[len];\n };\n String[] arr1 = fun.apply(10);\n System.out.println(arr1 + \", \" + arr1.length);\n\n //方法引用\n Function fun2 = String[]::new;\n String[] arr2 = fun2.apply(5);\n System.out.println(arr2 + \", \" + arr2.length);\n }\n}\n//控制檯打印:\n[Ljava.lang.String;@3d075dc0, 10\n[Ljava.lang.String;@7cca494b, 5\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"數組也是 Object 的子類對象,所以同樣具有構造器,只是語法稍有不同;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📚 Lambda和匿名內部類對比","attrs":{}}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"所需的類型不一樣:匿名內部類,需要的類型可以是類,抽象類,接口,Lambda表達式,需要的類型必須是接口;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"抽象方法數量不一樣:匿名內部類所需接口中抽象方法的數量隨意,Lambda表達式所需的接口只能有一個抽象方法;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}},{"type":"strong","attrs":{}}],"text":"實現原理不同:匿名內部類是在編譯後會形成class,Lambda表達式是在程序運行的時候動態生成class;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章