一、Lambda表達式簡介
什么是Lambda?
Lambda是JAVA 8添加的新特性,說白了,Lambda是一個匿名函數
為什么使用Lambda?
使用Lambda表達式可以對一個接口的方法進行非常簡潔的實現
Lambda對接口的要求
在JAVA8中 ,對接口加了一個新特性:default,可以使用default對接口方法進行修飾,被修飾的方法在接口中可以默認實現。
@FunctionalInterface
修飾函數式接口的,接口中的抽象方法只有一個
二、Lambda的基礎語法
1. 語法
主要關注:(返回值類型、方法名)參數列表、方法體。
/** * ():用來描述參數列表 * {}:用來描述方法體 有時可以省略 * ->: Lambda運算符 讀作goes to * 例 Test t=()->{System.out.println("hello word")}; 大括號可省略 */
2. 創建多個接口
/** * 無參數無返回值接口 */ @FunctionalInterface public interface LambdaNoneReturnNoneParmeter { void test(); } /** * 無返回值有單個參數 */ @FunctionalInterface public interface LambdaNoneReturnSingleParmeter { void test(int n); } /** * 無返回值 多個參數的接口 */ @FunctionalInterface public interface LambdaNoneReturnMutipleParmeter { void test(int a,int b); } /** * 有返回值 無參數接口 */ @FunctionalInterface public interface LambdaSingleReturnNoneParmeter { int test(); } /** * 有返回值 有單個參數的接口 */ @FunctionalInterface public interface LambdaSingleReturnSingleParmeter { int test(int n); } /** * 有返回值 有多個參數的接口 */ @FunctionalInterface public interface LambdaSingleReturnMutipleParmeter { int test(int a,int b);
3. 創建測試類
public class Syntax1 { public static void main(String[] args) { // 1.Lambda表達式的基礎語法 // Lambda是一個匿名函數 一般關注的是以下兩個重點 // 參數列表 方法體 /** * ():用來描述參數列表 * {}:用來描述方法體 * ->: Lambda運算符 讀作goes to */ // 無參無返回 LambdaNoneReturnNoneParmeter lambda1=()->{ System.out.println("hello word"); }; lambda1.test(); // 無返回值 單個參數 LambdaNoneReturnSingleParmeter lambda2=(int n)->{ System.out.println("參數是:"+n); }; lambda2.test(10); // 無返回值 多個參數 LambdaNoneReturnMutipleParmeter lambda3=(int a,int b)->{ System.out.println("參數和是:"+(a+b)); }; lambda3.test(10,12); // 有返回值 無參數 LambdaSingleReturnNoneParmeter lambda4=()->{ System.out.println("lambda4:"); return 100; }; int ret=lambda4.test(); System.out.println("返回值是:"+ret); // 有返回值 單個參數 LambdaSingleReturnSingleParmeter lambda5=(int a)->{ return a*2; }; int ret2= lambda5.test(3); System.out.println("單個參數,lambda5返回值是:"+ret2); //有返回值 多個參數 LambdaSingleReturnMutipleParmeter lambda6=(int a,int b)->{ return a+b; }; int ret3=lambda6.test(12,14); System.out.println("多個參數,lambda6返回值是:"+ret3); } } 輸出結果: hello word 參數是:10 參數和是:22 lambda4: 返回值是:100 單個參數,lambda5返回值是:6 多個參數,lambda6返回值是:26
三、語法精簡
1. 參數類型精簡
/** * 語法精簡 * 1.參數類型 * 由於在接口的抽象方法中,已經定義了參數的數量類型 所以在Lambda表達式中參數的類型可以省略 * 備注:如果需要省略類型,則每一個參數的類型都要省略,千萬不要一個省略一個不省略 */ LambdaNoneReturnMutipleParmeter lambda1=(int a,int b)-> { System.out.println("hello world"); }; 可以精簡為: LambdaNoneReturnMutipleParmeter lambda1=(a,b)-> { System.out.println("hello world"); };
2. 參數小括號精簡
/** * 2.參數小括號 * 如果參數列表中,參數的數量只有一個 此時小括號可以省略 */ LambdaNoneReturnSingleParmeter lambda2=(a)->{ System.out.println("hello world"); }; 可以精簡為: LambdaNoneReturnSingleParmeter lambda2= a->{ System.out.println("hello world"); };
3. 方法大括號精簡
/** * 3.方法大括號 * 如果方法體中只有一條語句,此時大括號可以省略 */ LambdaNoneReturnSingleParmeter lambda3=a->{ System.out.println("hello world"); }; 可以精簡為: LambdaNoneReturnSingleParmeter lambda3=a->System.out.println("hello world");
4. 大括號精簡補充
/** * 4.如果方法體中唯一的一條語句是一個返回語句 * 賊省略大括號的同時 也必須省略return */ LambdaSingleReturnNoneParmeter lambda4=()->{ return 10; }; 可以精簡為: LambdaSingleReturnNoneParmeter lambda4=()->10;
5. 多參數,有返回值 精簡
LambdaSingleReturnNoneParmeter lambda5=(a,b)->{ return a+b; }; 可以精簡為: LambdaSingleReturnMutipleParmeter lambda5=(a,b)->a+b;
四、Lambda語法進階
1. 方法引用(普通方法和靜態方法)
在實際應用過程中,一個接口在很多地方都會調用同一個實現,例如:
LambdaSingleReturnMutipleParmeter lambda1=(a,b)->a+b;
LambdaSingleReturnMutipleParmeter lambda2=(a,b)->a+b;
這樣一來每次都要寫上具體的實現方法 a+b,如果需求變更,則每一處實現都需要更改,基於這種情況,可以將后續的是實現更改為已定義的 方法,需要時直接調用就行
/** *方法引用: * 可以快速的將一個Lambda表達式的實現指向一個已經實現的方法 * 方法的隸屬者 如果是靜態方法 隸屬的就是一個類 其他的話就是隸屬對象 * 語法:方法的隸屬者::方法名 * 注意: * 1.引用的方法中,參數數量和類型一定要和接口中定義的方法一致 * 2.返回值的類型也一定要和接口中的方法一致 */ public class Syntax3 { public static void main(String[] args) { LambdaSingleReturnSingleParmeter lambda1=a->a*2; LambdaSingleReturnSingleParmeter lambda2=a->a*2; LambdaSingleReturnSingleParmeter lambda3=a->a*2; //簡化 LambdaSingleReturnSingleParmeter lambda4=a->change(a); //方法引用 LambdaSingleReturnSingleParmeter lambda5=Syntax3::change; } /** * 自定義的實現方法 */ private static int change(int a){ return a*2; } }
2. 方法引用(構造方法)
//實體類 public class Person { public String name; public int age; public Person() { System.out.println("Person的無參構造方法執行"); } public Person(String name, int age) { this.name = name; this.age = age; System.out.println("Person的有參構造方法執行"); } } //需求:兩個接口,各有一個方法,一個接口的方法需要引用Person的無參構造,一個接口的方法需要引用Person的有參構造 用於返回兩個Person對象,例: interface PersonCreater{ //通過Person的無參構造實現 Person getPerson(); } interface PersonCreater2{ //通過Person的有參構造實現 Person getPerson(String name,int age); } //那么可以寫作 public class Syntax4 { public static void main(String[] args) { PersonCreater creater=()->new Person(); //引用的是Person的無參構造 //PersonCreater接口的方法指向的是Person的方法 PersonCreater creater1=Person::new; //等價於上面的()->new Person() //實際調用的是Person的無參構造 相當於把接口里的getPerson()重寫成new Person()。 Person a=creater1.getPerson(); //引用的是Person的有參構造 PersonCreater2 creater2=Person::new; Person b=creater2.getPerson("張三",18); } } //注意:是引用無參構造還是引用有參構造 在於接口定義的方法參數
五、綜合練習
1. 集合排序案例
public class Exercise1 { public static void main(String[] args) { //需求:已知在一個ArrayList中有若干各Person對象,將這些Person對象按照年齡進行降序排列 ArrayList<Person> list=new ArrayList<>(); list.add(new Person("張三",10)); list.add(new Person("李四",12)); list.add(new Person("王五",13)); list.add(new Person("趙六",14)); list.add(new Person("李雷",11)); list.add(new Person("韓梅梅",8)); list.add(new Person("jack",10)); System.out.println("排序前:"+list); //將排列的依據傳入 具體的方法指向的是 內部元素的age相減 sort會依據結果的正負進行降序排列 //sort 使用提供的 Comparator對此列表進行排序以比較元素。 list.sort((o1, o2) -> o2.age-o1.age); System.out.println("排序后:"+list); } }
2. TreeSet排序案例
public class Exercise2 { public static void main(String[] args) { /**Treeset 自帶排序 * 但是現在不知道Person誰大誰小無法排序 * 解決方法: * 使用Lambda表達式實現Comparator接口,並實例化一個TreeSet對象 * 注意:在TreeSet中如果Comparator返回值是 0 會判斷這是兩個元素是相同的 會進行去重 * TreeSet<Person> set=new TreeSet<>((o1, o2) -> o2.age-o1.age); * 這個獲取的對象打印會少一個Person * 此時我們將方法修改 */ TreeSet<Person> set=new TreeSet<>((o1, o2) ->{ if(o1.age>=o2.age){ return -1; }else { return 1; } }); set.add(new Person("張三",10)); set.add(new Person("李四",12)); set.add(new Person("王五",13)); set.add(new Person("趙六",14)); set.add(new Person("李雷",11)); set.add(new Person("韓梅梅",8)); set.add(new Person("jack",10)); System.out.println(set); } }
3. 集合的遍歷
public class Exercise3 { public static void main(String[] args) { ArrayList<Integer> list=new ArrayList<>(); Collections.addAll(list,1,2,3,4,5,6,7,8,9); /** * list.forEach(Consumer<? super E> action) * api文檔解釋: 對 集合中的每個元素執行給定的操作,直到所有元素都被處理或動作引發異常。 * 將集合中的每一個元素都帶入到接口Consumer的方法accept中 然后方法accept指向我們的引用 * 輸出集合中的所有元素 * list.forEach(System.out::println); */ //輸出集合中所有的偶數 list.forEach(ele->{ if(ele%2==0){ System.out.println(ele); } }); } }
4. 刪除集合中滿足條件的元素
public class Exercise4 { public static void main(String[] args) { ArrayList<Person> list=new ArrayList<>(); list.add(new Person("張三",10)); list.add(new Person("李四",12)); list.add(new Person("王五",13)); list.add(new Person("趙六",14)); list.add(new Person("李雷",11)); list.add(new Person("韓梅梅",8)); list.add(new Person("jack",10)); //刪除集合中年齡大於12的元素 /** * 之前迭代器的做法 * ListIterator<Person> it = list.listIterator(); * while (it.hasNext()){ * Person ele=it.next(); * if(ele.age>12){ * it.remove(); * } * } */ /** * lambda實現 * 邏輯 * 將集合中的每一個元素都帶入到接口Predicate的test方法中, * 如果返回值是true,則刪除這個元素 */ list.removeIf(ele->ele.age>10); System.out.println(list); } }
5. 開辟一條線程 做一個數字的輸出
public class Exercise5 { public static void main(String[] args) { /** * 通過Runnable 來實例化線程 */ Thread t=new Thread(()->{ for(int i=0;i<100;i++){ System.out.println(i); } }); t.start(); } }
六、系統內置的函數式接口
public class FunctionalInterface { public static void main(String[] args) { // Predicate<T> : 參數是T 返回值boolean // 在后續如果一個接口需要指定類型的參數,返回boolean時可以指向 Predicate // IntPredicate int -> boolean // LongPredicate long -> boolean // DoublePredicate double -> boolean // Consumer<T> : 參數是T 無返回值(void) // IntConsumer int ->void // LongConsumer long ->void // DoubleConsumer double ->void // Function<T,R> : 參數類型T 返回值R // IntFunction<R> int -> R // LongFunction<R> long -> R // DoubleFunction<R> double -> R // IntToLongFunction int -> long // IntToDoubleFunction int -> double // LongToIntFunction long -> int // LongToDoubleFunction long -> double // DoubleToLongFunction double -> long // DoubleToIntFunction double -> int // Supplier<T> : 參數 無 返回值T // UnaryOperator<T> :參數T 返回值 T // BiFunction<T,U,R> : 參數 T、U 返回值 R // BinaryOperator<T> :參數 T、T 返回值 T // BiPredicate<T,U> : 參數T、U 返回值 boolean // BiConsumer<T,U> : 參數T、U 無返回值 /** * 常用的 函數式接口 * Predicate<T>、Consumer<T>、Function<T,R>、Supplier<T> */ } }
七、Lambda閉包
public class ClosureDemo { public static void main(String[] args) { /** * lambda的閉包會提升包圍變量的生命周期 * 所以局部變量 num在getNumber()方法內被 get()引用 不會在getNumber()方法執行后銷毀 * 這種方法可以在外部獲取到某一個方法的局部變量 */ int n=getNumber().get(); System.out.println(n); } private static Supplier<Integer> getNumber(){ int num=10; /** * Supplier supplier=()->num; * return supplier; */ return ()->{ return num; }; } } public class ClosureDemo2 { public static void main(String[] args) { int a=10; Consumer<Integer> c=ele->{ System.out.println(a+1); //System.out.println(ele); //System.out.println(a++); 會報錯 //在lambda中引用局部變量 這個變量必須是一個常量 }; //a++; 這樣也會導致內部報錯 //如果在內部已經引用局部變量 參數傳遞后 打印的還是 10 c.accept(1); } }
List<Map<String,Object>> treeList = reportservice.getClassificationTreeNode("****");
treeList.stream().filter(item -> item.get("parentId").equals("****")).collect(Collectors.toList());
注意:本篇文章內容來自於https://www.bilibili.com/video/av52431330/? 僅用於個人學習和總結,如有侵權,聯系刪除。
持續更新!!!
