Lambda表達式
為什麼使用Lambda表達式:
Lambda是一個匿名函數,我們可以把Lambda表達式理解為是一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。它是 JDK8 的一個新特性,可以取代大部分的匿名內部類,寫出更優雅的 Java 代碼,極大地優化代碼結構。
JDK 也提供了大量的內置函數式接口供我們使用,使得 Lambda 表達式的運用更加方便、高效。
Lambda表達式對接口的要求:
Lambda 表達式需要「函數式接口」的支持
函數式接口:接口中只有一個抽象方法的接口,稱為函數式接口。一般地,函數式接口都使用註解 @FunctionalInterface 修飾,來檢查是否是函數式接口。
Lambda表達式的基礎語法:
JAVA8 中引入了一個新的操作符 「->」 ,該操作符稱為 「箭頭操作符」 或 「Lambda操作符」。
箭頭操作符將 Lambda 表達式拆分成兩部分:
左側:Lambda 表達式的參數列表
右側:Lambda 表達式中所需要執行的功能,即 Lambda 體
示例:


import org.junit.Test; import java.util.Comparator; import java.util.function.Consumer; /** * Lambda 表達式測試 */ public class TestLambda1 { /** * 語法格式一: 無參數,無返回值 */ @Test public void test1(){ int num = 0;//jdk 1.7 前,必須是 final,jdk 1.8 以後,無需手動加 final ,默認就是final 的 Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello World!" + num); } }; r.run(); System.out.println("-------------------------------"); Runnable r1 = () -> System.out.println("Hello Lambda !! " + num); r1.run(); } /** * 語法格式二:有一個參數,並且無返回值 */ @Test public void test2(){ // /* //用一個Java的工具類作為案例。 @FunctionalInterface //函數式接口的註解 public interface Consumer<T> { void accept(T t); } */ Consumer<String> con = new Consumer<String>() { @Override public void accept(String s) { System.out.println("匿名內部類方式輸入的參數:"+s); } }; con.accept("Java 是最好的編程語言!"); System.out.println("-------------------------------"); Consumer<String> con1 = (s) -> System.out.println("Lambda 方式的輸入參數:"+s); con1.accept("Java 是最好的編程語言!"); } /** * 語法格式三:若只有一個參數,小括號可以省略不寫 */ @Test public void test3(){ Consumer<String> con1 = s -> System.out.println("Lambda 方式的輸入參數:"+s); con1.accept("Java 是最好的編程語言!"); } /** * 語法格式四:有兩個以上的參數,有返回值,並且 Lambda 體中有多條語句,則可以加上 {} */ @Test public void test4(){ Comparator<Integer> com = (x,y) -> { System.out.println("Java 是最好的編程語言!"); return Integer.compare(x, y); }; System.out.println(com.compare(3,4)); } /** * 語法格式五:若 Lambda 體中只有一條語句, return 和 {} 都可以省略不寫 */ @Test public void test5(){ Comparator<Integer> com = (x,y) -> Integer.compare(x, y); System.out.println(com.compare(3,4)); } /** * 語法格式六:Lambda 表達式的參數列表的數據類型可以省略不寫,因為JVM 編譯器通過上下文推斷出,數據類型,即「類型推斷」 */ @Test public void test6(){ Comparator<Integer> com = (Integer x,Integer y) -> Integer.compare(x, y); System.out.println(com.compare(3,4)); } }
View Code
類型推斷:上述 Lambda 表達式中的參數類型都是由編譯器推斷得出的。Lambda 表達式中無需指定類型,程序依然可以編譯,這是因為 javac 根據程序的上下文,在後台推斷出了參數的類型。Lambda 表達式的類型依賴於上下文環境,是由編譯器推斷出來的。這就是所謂的「類型推斷」。
Lambda表達式的應用示例:
示例1:對傳入的參數,進行運算,在實際調用方法的時候才知曉具體做什麼運算。


@FunctionalInterface public interface MyFun { /** * 根據傳入的值,進行運算,返回運算後的值。 * @param i * @return */ public Integer getValue(Integer i); } public class TestLambda2 { public static void main(String[] args) { //先用匿名內部類的方式 MyFun mf = new MyFun() { @Override public Integer getValue(Integer i) { System.out.println("我是最原始的匿名內部類實現的運算(返回:參數 * 100)..."); return i * 100; } }; //對 20 進行 * 100 的運算 System.out.println(mf.getValue(20)); System.out.println("-------------------------------"); //轉換成Lambda表達式方式 MyFun mf1 = (i) -> { System.out.println("Lambda 表達式實現運算(返回:參數 * 100)..."); return i * 100; }; System.out.println(mf1.getValue(20)); //Lambda表達式可以用最簡便的代碼,實現各種運算 //例: MyFun mf2 = i -> i + 2; MyFun mf3 = i -> i * 2; MyFun mf4 = i -> i / 2; MyFun mf5 = i -> i - 2; //等等.................... } }
View Code
示例2:用 Collections.sort() 方法對集合排序。


import lombok.Data; @Data public class Employee{ private String name; private Integer age; private Integer gender; } import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class TestLambda3 { public static void main(String[] args) { List<Employee> emps = new ArrayList<>(); emps.add(new Employee("張三",10,0)); emps.add(new Employee("李四",13,1)); emps.add(new Employee("王麻子",8,0)); emps.add(new Employee("王五",18,0)); emps.add(new Employee("趙六",13,0)); //原始方法排序 Collections.sort(emps, new Comparator<Employee>() { @Override public int compare(Employee o1, Employee o2) { //按照年齡排序 return o1.getAge() - o2.getAge(); } }); System.out.println(emps); System.out.println("-------------------------------"); //Lambda表達式方式 // 排序1:按照年齡排 Collections.sort(emps,(o1,o2)->o2.getAge() - o1.getAge()); System.out.println(emps); System.out.println("-------------------------------"); //排序2:如果年齡相同,則按照性別排序 Collections.sort(emps,(o1,o2)->{ if((o1.getAge() - o2.getAge()) == 0){ return o2.getGender() - o1.getGender(); } return o1.getAge() - o2.getAge(); }); System.out.println(emps); } }
View Code
當我們需要用Lambda表達式來實現一個自己的需求,就得自己去創建一個接口,這樣還是比較麻煩的。實際上,我們完全可以不用自己創建接口,使用Java8自帶的函數式接口來滿足我們的需求。
JAVA8內置的四大函數式接口:
函數式接口 | 參數類型 | 返回類型 | 用途 |
Consumer<T> 消費型接口 |
T | void | 對類型為 T 的對象應用操作,包含方法:void accept(T t); |
Supplier<T> 供給型接口 |
無 | T | 返回類型為T 的對象,包含方法:T get(); |
Function<T,R> 函數型接口 |
T | R | 對類型為 T 的對象應用操作,並返回結果。結果是R類型的對象。包含方法:R apply(T t); |
Predicate<T> 斷定型接口 |
T | boolean | 確定類型為T的對象是否滿足某約束,並返回boolean值。包含方法:boolean test(T t); |
內置函數式接口示例:


import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; public class TestLambda4 { /** * Consumer<T> 消費型接口 */ @Test public void test1(){ happy(20000.0,m -> System.out.println("你要去做大保健,每次"+m+"元")); } public void happy(Double money, Consumer<Double> consumer){ consumer.accept(money); } /** * Supplier<T> 供給型接口 */ @Test public void test2(){ List<Integer> list = getNumList(25, () -> (int) (Math.random() * 100)); System.out.println(list); } /** * 獲取指定數量的整數集合 * @param num * @param supplier * @return */ public List<Integer> getNumList(int num,Supplier<Integer> supplier){ List<Integer> list = new ArrayList<>(); for(int i=0;i<num ;i++){ Integer result = supplier.get(); list.add(result); } return list; } /** * Function<T, R> 函數型接口 */ @Test public void test3(){ String newStr = strHandler("\t\t\n Java 好牛逼呀 ", (str) -> str.trim()); System.out.println(newStr); String subStr = strHandler("Java is so so cool...", (str) -> str.toUpperCase()); System.out.println(subStr); } /** * 需求:用於處理字符串 * @param str * @param fun * @return */ public String strHandler(String str, Function<String, String> fun){ return fun.apply(str); } /** * Predicate<T> 斷定型接口 */ @Test public void test4(){ List<String> list = Arrays.asList("Java", "Python", "Lambda", "I love u", "ok"); List<String> strList = filterStr(list, (s) -> s.length() > 4); System.out.println(strList); } /** * 返回滿足條件的字符串集合 * @param list * @param pre * @return */ public List<String> filterStr(List<String> list, Predicate<String> pre){ List<String> strList = new ArrayList<>(); for (String str : list) { if(pre.test(str)){ strList.add(str); } } return strList; } }
View Code
其他函數式接口:
方法引用、構造器引用、數組引用:
方法引用:
若 Lambda 體重的內容有方法已經實現了,我們可以使用「方法引用」。可以理解為方法引用就是 Lambda 表達式的另外一種表現形式。
方法引用主要的三種語法格式:
對象 :: 實例方法名
類 :: 靜態方法名
類 :: 實例方法名
注意:
1. Lambda 體中調用方法的 【參數列表】與【返回值類型】,要與 函數式接口中 抽象方法 的 【參數列表】和【返回值類型】保持一致。
2. 若 Lambda 參數列表中的 第一參數 是 實例方法的調用者,而 第二個參數 是 實例方法的 參數 時,則可以使用 類 :: 實例方法名(ClassName :: method)
示例:


import org.junit.Test; import java.util.Comparator; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Supplier; public class TestLambda5 { /** * 方法引用測試 對象 :: 實例方法名 */ @Test public void test1(){ Consumer<String> con = (str) -> System.out.println("我是參數:"+str); con.accept("Hello Lambda"); Consumer<String> con1 = System.out::println; con1.accept("Hello 方法引用..."); // 需要實現的抽象方法的參數列表和返回值:void accept(String str); accept() 方法的參數為 String ,返回值為 void //Lambda體中引用的方法的 參數列表 與 返回值類型 : System.out.println("");System.out 對象的 println() 方法的參數為 String , 返回值為 void //在上面這種情況下,(str) -> System.out.println("我是參數:"+str); 就可以直接寫成 System.out::println } /** * 方法引用測試 對象 :: 實例方法名 */ @Test public void test2(){ Employee emp = new Employee("周三",22,1); Supplier<String> sup1 = ()-> emp.getName(); //Supplier<String> sup1 = emp :: getName; //與上面等價 System.out.println(sup1.get()); System.out.println("-------------------------------"); //Supplier<Integer> sup2 = () -> emp.getAge(); //與下面等價 Supplier<Integer> sup2 = emp::getAge; //sup2.get() 和 emp.getAge() 參數列表都為 Null ,返回值都是Integer ,所以可以用兩種表現形式。 System.out.println(sup2.get()); } /** * 方法引用測試 類 :: 靜態方法名 */ @Test public void test3(){ Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y); System.out.println(com1.compare(10,20)); System.out.println("-------------------------------"); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(122,22)); } /** * 方法引用測試 類 :: 實例方法名 */ @Test public void test4(){ BiPredicate<String,String> bp1 = (x,y) -> x.equals(y); System.out.println(bp1.test("abc","abc")); System.out.println("-------------------------------"); BiPredicate<String,String> bp2 = String::equals; System.out.println(bp1.test("abc","abc")); } }
View Code
構造器引用:
與函數式接口相結合,自動與函數式接口中方法兼容。可以把構造器引用賦值給定義的方法,與構造器參數列表要與接口中抽象方法的參數列表一致!
格式:ClassName :: new
注意:
需要調用的 構造器 的 參數列表 要與 函數式接口 中 抽象方法 的 參數列表 保持一致!
示例:


import org.junit.Test; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; /** * 構造器 引用 測試 * 格式:ClassName :: new */ public class TestLambda6 { /** * 調用無參構造器 */ @Test public void test1(){ Supplier<Employee> supplier = () -> new Employee(); System.out.println(supplier.get()); System.out.println("-------------------------------"); Supplier<Employee> sup1 = Employee::new;//sup1.get() 沒有參數,調用的new Employee() 也是無參構造器。 System.out.println(sup1.get()); } /** * 調用一個參數的構造器 */ @Test public void test3(){ Function<String,Employee> fun = (name) -> new Employee(name); System.out.println(fun.apply("張麻子")); System.out.println("-------------------------------"); Function<String,Employee> fun1 = Employee::new;//fun1.apply("") 有一個參數,調用的new Employee("")一個參數的構造器。 System.out.println(fun1.apply("王二麻子")); } /** * 調用兩個參數的構造器 */ @Test public void test4(){ BiFunction<String,Integer,Employee> fun = (name,age) -> new Employee(name,age); System.out.println(fun.apply("張麻子",12)); System.out.println("-------------------------------"); BiFunction<String,Integer,Employee> fun1 = Employee::new;//fun1.apply("",int) 有兩個參數,調用的new Employee("",int)兩個參數的構造器。 System.out.println(fun1.apply("王二麻子",20)); } }
View Code
數組引用:
格式:type[] :: new
示例:


/** * 數組引用 */ @Test public void test5() { Function<Integer, String[]> fun1 = (x) -> new String[x]; System.out.println(fun1.apply(10).length); System.out.println("-------------------------------"); Function<Integer, String[]> fun2 = String[]::new; System.out.println(fun1.apply(20).length); }
View Code