1. 程式人生 > >JDK新特性-Lambda表示式的神操作

JDK新特性-Lambda表示式的神操作

## 一、Lambda表示式的介紹 - Lambda表示式是 Java8 中最重要的新功能之一。使用 Lambda 表達 式可以替代只有一個抽象函式的介面實現,告別匿名內部類,程式碼看 起來更簡潔易懂。Lambda表示式同時還提升了對集合、框架的迭代、 遍歷、過濾資料的操作。 - lambda表示式可以替代只有一個抽象函式的介面實現,告別匿名內部類,程式碼看起來更簡潔易懂 - lambda表示式同時還提升了對集合、框架的迭代、遍歷、過濾資料的操作 - lambda可以極大的減少程式碼冗餘,同時代碼的可讀性要好過冗長的內部類,匿名類 例如以前我們使用匿名內部類來實現程式碼: ```java Runnable runnable = new Runnable() { @Override public void run() { System.out.println("running1 ....."); } }; runnable.run(); ``` 使用lambda表示式實現更簡潔的程式碼: ```java Runnable runnable3 = ()-> System.out.println("running2...."); runnable3.run(); ``` **lambda表示式語法:** ```java LambdaParameters -> LambdaBody ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201107094149930.png#pic_center) args -> expr或者(object ... args)-> {函式式介面抽象方法實現邏輯} ​ 1、()引數的個數,根據函式式接口裡面抽象的引數個數來決定,當引數只有一個的時候,()可以省略 ​ 2、當expr邏輯非常簡單的時候,{}和return可以省略 **案例說明:** ```java public static void main(String[] args) throws Exception { Callable c1 = new Callable() { @Override public String call() throws Exception { return "muxiaonong"; } }; System.out.println(c1.call()); Callable c2 = ()->{return "muxiaonong2";}; System.out.println(c2.call()); //邏輯很簡單的時候省略 {} 和 return Callable c3 = ()->"muxiaonong3"; System.out.println(c3.call()); } ``` ## 二、Lambda表示式的特點 - 函數語言程式設計 - 引數型別自動推斷 - 程式碼量少,簡潔 ## 三、Lambda表示式案例 **實現方式列表:** ```java ​ ()->{} ​ ()->{System.out.println(1);} ​ ()->System.out.println(1) ​ ()->{return 100;} ​ ()->100 ​ ()->null ​ (int x)->{return x+1;} ​ (int x)->x+1 ​ (x)->x+1 ​ x->x+1 ``` 案例1:執行緒實現方式: ```java public static void main(String[] args) { //匿名內部類方式 new Thread(new Runnable() { @Override public void run() { System.out.println("runing1.........."); } }); //Lambda表示式方式 new Thread(() -> {System.out.println("runing2.....");}).start(); } ``` 案例2:集合遍歷實現方式 ```java public static void main(String[] args) { List list = Arrays.asList("java","python","scala","javascript"); //普通匿名內部類方式 Collections.sort(list, new Comparator() { @Override public int compare(String o1, String o2) { return o1.length() - o2.length(); } }); //Lambda方式 Collections.sort(list,(a,b) -> a.length() - b.length()); list.forEach(System.out::println); } ``` ## 四、Lambda表示式的應用場景 重要的事情說三遍:**任何有函式式介面的地方 * 3** **什麼是函式式介面:** ```只有一個抽象方法(Object類中的方法除外)的介面是函式式介面``` ## 五、Lambda表示式實際應用 #### 5.1 無參實體類模擬 **模擬資料庫連線層:** ```java @FunctionalInterface public interface StudentDao { void insert(Student student); } ``` 實體類 ```java /** @Author mxn * @Description 學生實體類 * @Date 10:19 2020/11/7 * @Param * @return **/ public class Student { } ``` ```java public static void main(String[] args) { StudentDao sd1 = new StudentDao() { @Override public void insert(Student student) { System.out.println("插入學生1"); } }; StudentDao sd2 = (student)->{ System.out.println("student: "+student); }; StudentDao sd3 = (Student student)-> System.out.println("student3:"+student); sd1.insert(new Student()); //輸出 插入學生1 sd2.insert(new Student());// 輸出 sd3.insert(new Student());// 輸出 } ``` #### 5.2 有參實體類模擬 實體類 ```java /** @Author mxn * @Description * @Date 10:26 2020/11/7 * @Param * @return **/ public class Teacher { } ``` 介面模擬層 ```java @FunctionalInterface public interface TeacherDao { int get(Teacher teacher); } ``` 實現層 ```java public static void main(String[] args) { TeacherDao td1 = new TeacherDao() { @Override public int get(Teacher teacher) { return 1; } }; TeacherDao td2 = (teacher)->{return 2;}; TeacherDao td3 = (Teacher teacher)->{return 3;}; TeacherDao td4 = (teacher)->4; TeacherDao td5 = (Teacher teacher)->5; System.out.println(td1.get(new Teacher()));//輸出 1 System.out.println(td2.get(new Teacher()));//輸出 2 System.out.println(td3.get(new Teacher()));//輸出 3 System.out.println(td4.get(new Teacher()));//輸出 4 System.out.println(td5.get(new Teacher()));//輸出 5 } ``` ## 六、函式式介面 ```Supplier:```代表一個輸出 ```Consumer:```代表一個輸入 ```BiConsumer:```代表兩個輸入 ```Function:```代表一個輸入,一個輸出(一般輸入和輸出是不同型別的) ```UnaryOperator:```代表一個輸入,一個輸出(輸入和輸出是相同型別的) ```BiFunction:```代表兩個輸入,一個輸出(一般輸入和輸出是不同型別的) ```BinaryOperator:```代表兩個輸入,一個輸出(輸入和輸出是相同型別的) **在Java中提供了一系列的函式式介面,用來接受後續傳入的邏輯,但是對輸入和輸出有要求** #### 6.1 Supplier:代表一個輸出 ```java Supplier s1 = ()->{return "muxiaonong";}; Supplier s2 = ()->"muxiaonong2"; System.out.println(s1.get());//輸出 muxiaonong System.out.println(s2.get());//輸出 muxiaonong2 ``` #### 6.2 Consumer:代表一個輸入 ```java Consumer c11 = (str) -> System.out.println(str); c11.accept("beijing");//輸出 beijing ``` #### 6.3 BiConsumer:代表兩個輸入 ```java BiFunction bf = (a,b)->a.length()+b.length(); System.out.println(bf.apply("大吉大利", "今晚吃雞"));//輸出一個字串長度 8 ``` #### 6.4 Function:代表一個輸入,一個輸出 ```java // Function 用來接收後面的函式的實現,規定必須有一個輸入(String)有一個輸出(Integer) Function f1 = (str)->{return str.length();}; System.out.println(f1.apply("abcdefg"));//輸出長度 7 ``` ## 七、方法的引用 - 方法引用是用來直接訪問類或者例項的已經存在的方法或者構造方法,方法引用提供了一種引用而不執行方法的方式,如果抽象方法的實現恰好可以使用呼叫另外一個方法來實現,就有可能可以使用方法引用 #### 7.1 方法引用的分類 型別| 語法| 對應的lambda表示式 -------- | ----- | ----- 靜態方法引用| 類名::staticMethod | (args) -> 類名.staticMethod(args) 例項方法引用 | inst::instMethod| (args) -> inst.instMethod(args) 物件方法引用| 類名::instMethod| (inst,args) -> 類名.instMethod(args) 構造方法引用| 類名::new| (args) -> new 類名(args) #### 7.2 靜態方法引用 - **靜態方法引用:** 如果函式式介面的實現恰好可以通過 **呼叫一個靜態方法** 來實現,那麼就可以使用靜態方法引用 ```java /** * @program: lambda * @ClassName Test2 * @description: * @author: muxiaonong * @create: 2020-10-28 22:15 * @Version 1.0 **/ public class Test2 { //無參靜態方法 static String put(){ System.out.println("put....."); return "put"; } //有參靜態方法 public static void getSize(int size){ System.out.println(size); } //有參 有返回值靜態方法 public static String toUpperCase(String str){ return str.toUpperCase(); } //兩個入參,一個返回值靜態方法 public static Integer getLength(String str,String str2){ return str.length()+str2.length(); } public static void main(String[] args) { //無參靜態方法-普通呼叫 System.out.println(put());//輸出put //無參靜態方法-原生呼叫 Supplier s1 = ()-> Test2.put(); System.out.println(s1.get());//輸出put //無參靜態方法-靜態方法引用 Supplier s2 = Test2::put; System.out.println(s2.get());//輸出put //無參靜態方法-內部類呼叫 Supplier s3 = Fun::hehe; System.out.println(s3.get()); //輸出hehe // 有參靜態方法-靜態方法引用 Consumer c1 = Test2::getSize; Consumer c2 = (size)-> Test2.getSize(size); c1.accept(123); c2.accept(111); //有參有返回值靜態方法 Function f1 = (str)->str.toUpperCase(); Function f2 = (str)-> Test2.toUpperCase(str); Function f3 = Test2::toUpperCase; Function f4 = Test2::toUpperCase; System.out.println(f1.apply("abc"));//輸出 ABC System.out.println(f2.apply("abc"));//輸出 ABC System.out.println(f3.apply("abc"));//輸出 ABC System.out.println(f4.apply("abc"));//輸出 ABC // 兩個引數 一個返回值 函式式介面 BiFunction bf = (a, b)->a.length()+b.length(); BiFunction bf2 = Test2::getLength; System.out.println(bf2.apply("abc", "def"));//輸出 6 System.out.println(bf.apply("abc", "def"));//輸出 6 } //內部類 class Fun { public static String hehe(){ return "hehe"; } public static String toUpperCase(String str){ return str.toUpperCase(); } } } ``` #### 7.3 例項方法引用 - **例項方法引用:** 如果函式式介面的實現恰好可以通過呼叫一個例項的例項方法來實現,那麼就可以使用例項方法引用 ```java public class Test3 { //例項無參方法 public String put(){ return "put..."; } //例項有參方法 public void getSize(int size){ System.out.println("size:"+size); } //例項有參有返回值方法 public String toUpperCase(String str){ return str.toUpperCase(); } public static void main(String[] args) { //例項無參方法返回-普通呼叫 System.out.println(new Test3().put());//輸出 put... Supplier s1 = ()->new Test3().put(); Supplier s2 = ()->{return new Test3().put();}; Supplier s3 = new Test3()::put; System.out.println(s1.get());//輸出 put... System.out.println(s2.get());//輸出 put... System.out.println(s3.get());//輸出 put... //唯一的建立一個test3物件 Test3 test = new Test3(); Consumer c1 = (size)->new Test3().getSize(size); Consumer c2 = new Test3()::getSize; Consumer c3 = test::getSize; c1.accept(123);//輸出 size:123 c2.accept(123);//輸出 size:123 c3.accept(123);//輸出 size:123 Function f1 = (str)->str.toUpperCase(); Function f2 = (str)->test.toUpperCase(str); Function f3 = new Test3()::toUpperCase; Function f4 = test::toUpperCase; System.out.println(f1.apply("abc"));//輸出 ABC System.out.println(f2.apply("abc"));//輸出 ABC System.out.println(f3.apply("abc"));//輸出 ABC System.out.println(f4.apply("abc"));//輸出 ABC } } ``` #### 7.4 物件方法引用 - **物件方法引用:** 抽象方法的第一個引數型別剛好是例項方法的型別,抽象方法剩餘的引數恰好可以當做例項方法的引數。如果函式式介面的實現能由上面說的例項方法呼叫來實現的話,那麼就可以使用物件方法引用 ```java /** @Author mxn * @Description //TODO 物件方法引用 * @Date 14:26 2020/11/7 * @Param * @return **/ public class Test4 { public static void main(String[] args) { Consumer c1 = (too)->new Too().foo(); c1.accept(new Too());//輸出 foo Consumer c2 = (Too too) ->new Too2().foo(); c2.accept(new Too());//輸出 foo---too2 Consumer c3 = Too::foo; c3.accept(new Too());//輸出 foo BiConsumer bc = (too2,str)->new Too2().show(str); BiConsumer bc2 = Too2::show; bc.accept(new Too2(),"abc"); bc2.accept(new Too2(),"def"); BiFunction bf1 = (e,s)->new Exec().test(s); bf1.apply(new Exec(),"abc"); BiFunction bf2 = Exec::test; bf2.apply(new Exec(),"def"); } } class Exec{ public int test(String name){ return 1; } } class Too{ public Integer fun(String s){ return 1; } public void foo(){ System.out.println("foo"); } } class Too2{ public Integer fun(String s){ return 1; } public void foo(){ System.out.println("foo---too2"); } public void show(String str){ System.out.println("show ---too2"+str); } } ``` #### 7.5 構造方法引用 - **構造方法引用:** 如果函式式介面的實現恰好可以通過呼叫一個類的構造方法來實現,那麼就可以使用構造方法引用 ```java /** @Author mxn * @Description //TODO 構造方法引用 * @Date 14:27 2020/11/7 * @Param * @return **/ public class Test5 { public static void main(String[] args) { Supplier s1 = ()->new Person(); s1.get();//輸出 呼叫無參的構造方法 Supplier s2 = Person::new; s2.get();//輸出 呼叫無參的構造方法 Supplier s3 = ArrayList::new; Supplier s4 = HashSet::new; Supplier s5 = Thread::new; Supplier s6 = String::new; Consumer c1 = (age)->new Account(age); Consumer c2 = Account::new; c1.accept(123);// 輸出 age 引數構造123 c2.accept(456);//輸出 age 引數構造456 Function f1 = (str)->new Account(str); Function f2 = Account::new; f1.apply("abc");//輸出 str 引數構造abc f2.apply("def");//輸出 str 引數構造def } } class Account{ public Account(){ System.out.println("呼叫無參構造方法"); } public Account(int age){ System.out.println("age 引數構造" +age); } public Account(String str){ System.out.println("str 引數構造" +str); } } class Person{ public Person(){ System.out.println("呼叫無參的構造方法"); } } ``` ## 八、小結 - JAVA 8 引入 Lambda表示式是接收了函數語言程式設計語言的思想,和指令式程式設計相比,函數語言程式設計強調函式的計算比指令的執行重要。 - lambda表示式可以使程式碼看起來簡潔,但一定程度上增加了程式碼的可讀性以及除錯的複雜性,所以在使用時應儘量是團隊都熟悉使用,要麼乾脆就別用,不然維護起來是件較痛苦的事,今天的小知識就到這裡了,有問題的小夥伴可以在下方進行留言,大家