函數式編程思想概述 在數學中,函數就是有輸入量、輸出量的一套計算方案,也就是“拿什麼東西做什麼事情”。相對而言,面向對象過 分強調“必須通過對象的形式來做事情”,而函數式思想則儘量忽略面向對象的複雜語法——強調做什麼,而不是以 什麼形式做。 面向對象的思想: 做一件事情,找一個能解決這個事情的對象, ...
函數式編程思想概述
在數學中,函數就是有輸入量、輸出量的一套計算方案,也就是“拿什麼東西做什麼事情”。相對而言,面向對象過 分強調“必須通過對象的形式來做事情”,而函數式思想則儘量忽略面向對象的複雜語法——強調做什麼,而不是以 什麼形式做。
面向對象的思想: 做一件事情,找一個能解決這個事情的對象,調用對象的方法,完成事情.
函數式編程思想: 只要能獲取到結果,誰去做的,怎麼做的都不重要,重視的是結果,不重視過程
冗餘的Runnable代碼
傳統寫法:當需要啟動一個線程去完成任務時,通常會通過 java.lang.Runnable 介面來定義任務內容,並使用 java.lang.Thread 類來啟動該線程
1 package demosummary.lambda; 2 3 public class RunnableDemo1 { 4 public static void main(String[] args) { 5 //匿名內部類 6 Runnable task = new Runnable() { 7 @Override 8 public void run() { 9 System.out.println("多線程任務執行"); 10 } 11 }; 12 new Thread(task).start();//啟動線程 13 } 14 }
對上述代碼分析:
1.Thread 類需要 Runnable 介面作為參數,其中的抽象 run 方法是用來指定線程任務內容的核心
2.為了指定 run 的方法體,不得不需要 Runnable 介面的實現類
3.為了省去定義一個 RunnableImpl 實現類的麻煩,不得不使用匿名內部類;
4.必須覆蓋重寫抽象 run 方法,所以方法名稱、方法參數、方法返回值不得不再寫一遍,且不能寫錯;
5.而實際上,似乎只有方法體才是關鍵所在
lambda更優的寫法
1 package demosummary.lambda; 2 3 /** 4 * Lambda寫法 5 */ 6 public class LambdaDemo1 { 7 public static void main(String[] args) { 8 new Thread(() -> System.out.println("多線程任務執行")).start();//啟動線程 9 } 10 }
以上代碼和上一個代碼是同樣的執行效果,lambda寫法需要在1.8以上的編譯才能通過
回顧匿名內部類
1.使用實現類
要啟動一個線程,需要創建一個 Thread 類的對象並調用 start 方法。而為了指定線程執行的內容,需要調用 Thread 類的構造方法:public Thread(Runnable target)
為了獲取 Runnable 介面的實現對象,可以為該介面定義一個實現類 RunnableImpl :
1 package demosummary.lambda; 2 3 public class RunnableImpl implements Runnable{ 4 @Override 5 public void run() { 6 System.out.println("多線程任務執行"); 7 } 8 }
然後創建該實現類的對象作為 Thread 類的構造參數:
1 package demosummary.lambda; 2 3 public class RunnableImplTest { 4 public static void main(String[] args) { 5 //創建RunnableImpl對象 6 RunnableImpl ri = new RunnableImpl(); 7 //啟動線程 8 new Thread(ri).start(); 9 } 10 }
2.使用匿名內部類
這個 RunnableImpl 類只是為了實現 Runnable 介面而存在的,而且僅被使用了唯一一次,所以使用匿名內部類的 語法即可省去該類的單獨定義,即匿名內部類:
1 package demosummary.lambda; 2 3 /** 4 * 使用匿名內部類 5 */ 6 public class RunnableImplInit { 7 public static void main(String[] args) { 8 new Thread(new Runnable() { 9 @Override 10 public void run() { 11 System.out.println("多線程任務執行"); 12 } 13 }).start(); 14 } 15 }
對上述匿名內部類方法分析
仔細分析該代碼中的語義, Runnable 介面只有一個 run 方法的定義:public abstract void run();
無參數:不需要任何條件即可執行該方案。
無返回值:該方案不產生任何結果。
代碼塊(方法體):該方案的具體執行步驟
而使用lambda語法要更加簡單:
() ‐> System.out.println("多線程任務執行!")
前面的一對小括弧即 run 方法的參數(無),代表不需要任何條件;
中間的一個箭頭代表將前面的參數傳遞給後面的代碼;
後面的輸出語句即業務邏輯代碼。
匿名內部類的好處與弊端
一方面,匿名內部類可以幫我們省去實現類的定義;另一方面,匿名內部類的語法——確實太複雜了
Lambda表達式的標準格式
Lambda省去面向對象的條條框框,格式由3個部分組成:
1.一些參數 2.一個箭頭 3.一段代碼
Lambda表達式的標準格式為:(參數類型 參數名稱) ‐> { 代碼語句 }
格式說明:
1.小括弧內的語法與傳統方法參數列表一致:無參數則留空;
2.多個參數則用逗號分隔。 -> 是新引入的語法格式,代表指向動作。
3.大括弧內的語法與傳統方法體要求基本一致。
Lambda省略格式
省略規則:
1. 小括弧內參數的類型可以省略;
2. 如果小括弧內有且僅有一個參,則小括弧可以省略;
3. 如果大括弧內有且僅有一個語句,則無論是否有返回值,都可以省略大括弧、return關鍵字及語句分號
Lambda的使用前提
Lambda的語法非常簡潔,完全沒有面向對象複雜的束縛。但是使用時有幾個問題需要特別註意
1. 使用Lambda必須具有介面,且要求介面中有且僅有一個抽象方法。 無論是JDK內置的 Runnable 、 Comparator 介面還是自定義的介面,只有當介面中的抽象方法存在且唯一 時,才可以使用Lambda。
2. 使用Lambda必須具有上下文推斷。 也就是方法的參數或局部變數類型必須為Lambda對應的介面類型,才能使用Lambda作為該介面的實例。
備註:有且僅有一個抽象方法的介面,稱為“函數式介面”。
使用lambda標準格式(無參無返回)
給定一個廚子 Cook 介面,內含唯一的抽象方法 makeFood ,且無參數、無返回值。如下:
1 package demosummary.lambda; 2 3 /** 4 * 給定一個廚子 Cook 介面,內含唯一的抽象方法 makeFood ,且無參數、無返回值。如下: 5 */ 6 public interface Cook { 7 void makeFood(); 8 }
在下麵的代碼中,請使用Lambda的標準格式調用 invokeCook 方法,列印輸出“吃飯啦!”字樣:
1 package demosummary.lambda; 2 3 /** 4 * 在下麵的代碼中,請使用Lambda的標準格式調用 invokeCook 方法,列印輸出“吃飯啦!”字樣: 5 */ 6 public class InvokeCook { 7 public static void main(String[] args) { 8 // invokeCook(() -> { 9 // System.out.println("吃飯了"); 10 // }); 11 12 //省略格式 13 invokeCook(() -> System.out.println("吃飯了")); 14 } 15 16 private static void invokeCook(Cook cook) { 17 cook.makeFood(); 18 } 19 }
備註:小括弧代表 Cook 介面 makeFood 抽象方法的參數為空,大括弧代表 makeFood 的方法體。
使用Lambda標準格式(有參有返回)
給定一個計算器 Calculator 介面,內含抽象方法 calc 可以將兩個int數字相加得到和值:
1 package demosummary.lambda; 2 3 public interface Calculator { 4 int cal(int a, int b); 5 }
在下麵的代碼中,請使用Lambda的標準格式調用 invokeCalc 方法,完成120和130的相加計算:
1 package demosummary.lambda; 2 3 public class invokeCal { 4 public static void main(String[] args) { 5 // invokeCal(120,130,(int a , int b) -> { 6 // return a + b; 7 // }); 8 9 //省略格式 10 invokeCal(120,130,(int a , int b) -> a + b); 11 } 12 13 public static void invokeCal(int a , int b , Calculator calculator){ 14 int result = calculator.cal(a, b); 15 System.out.println("輸出結果:"+result); 16 } 17 }
Lambda的參數和返回值
需求: 1.使用數組存儲多個Person對象 2.對數組中的Person對象使用Arrays的sort方法通過年齡進行升序排序
1 package demosummary.lambda; 2 3 public class Person { 4 private String name; 5 private int age; 6 7 public Person() { 8 } 9 10 public Person(String name, int age) { 11 this.name = name; 12 this.age = age; 13 } 14 15 public String getName() { 16 return name; 17 } 18 19 public void setName(String name) { 20 this.name = name; 21 } 22 23 public int getAge() { 24 return age; 25 } 26 27 public void setAge(int age) { 28 this.age = age; 29 } 30 31 @Override 32 public String toString() { 33 return "Person{" + 34 "name='" + name + '\'' + 35 ", age=" + age + 36 '}'; 37 } 38 }
1 package demosummary.lambda; 2 3 import java.util.Arrays; 4 5 public class LambdaComparator { 6 public static void main(String[] args) { 7 Person[] array = { 8 new Person("古力娜扎",22), 9 new Person("迪麗熱巴",21), 10 new Person("歐陽娜娜",19) 11 }; 12 13 // Arrays.sort(array,(Person a, Person b)->{ 14 // return a.getAge() - b.getAge(); 15 // }); 16 17 //省略格式 18 Arrays.sort(array,(Person a, Person b) -> a.getAge() - b.getAge()); 19 20 for (Person person : array) { 21 System.out.println(person); 22 } 23 } 24 }