lambda表達式
Lambda表達式的基本語法::(parameters) -> expression 或 (parameters) ->{ statements; }
- () -> 代表了 lambda的一個表達式
- 單行代碼無需寫return (無論函數式接口有沒有返回值),花括號
- 多行代碼必須寫花括號,有返回值的一定要寫返回值
- 單行代碼且有參數的情況下可以不寫 () 如 s->System.out.println(s)
- (T t)中的參數類型可寫可不寫
例子
// 1. 不需要參數,返回值爲 5 () -> 5 // 2. 接收一個參數(數字類型),返回其2倍的值 x -> 2 * x // 3. 接受2個參數(數字),並返回他們的差值 (x, y) -> x – y // 4. 接收2個int型整數,返回他們的和 (int x, int y) -> x + y // 5. 接受一個 string 對象,並在控制檯打印,不返回任何值(看起來像是返回void) (String s) -> System.out.print(s)
主要使用場景
1. 簡化匿名類的編碼
2. 減少不必要的方法創建
3. 事件處理
4. stream中使用
函數式編程
函數式接口
1 .概念
函數式接口在java中是指:有且僅有一個抽象方法的接口
函數式接口,即適用於函數式編程場景的接口。而java中的函數式編程體現就是Lambda,所以函數式接口就是可以適用於Lambda使用的接口。只有確保接口中有且僅有一個抽象方法,Java中的Lambda才能順利地進行推導。
備註:“語法糖"是指使用更加方便,但是原理不變的代碼語法。例如在遍歷集合時使用的for-each語法,其實底層的實現原理仍然是迭代器,這便是“語法糖”。從應用層面來講,Java中的Lambda可以被當做是匿名內部類的“語法糖”,但是二者在原理上是不同的。
2. 格式
接口中只能存在一個抽象方法
@FunctionalInterface interface Callback { void callback(); }
3.@FunctionalInterface註解
一旦使用該註解來定義接口,編譯器將會強制檢查該接口是否確實有且僅有一個抽象方法,否則將會報錯。需要注意的是,即使不使用該註解,只要滿足函數式接口的定義,這仍然是一個函數式接口,使用起來都一樣。(該接口是一個標記接口)
- 被@FunctionalInterface註釋的接口,滿足函數式接口的約束。
- 沒有被@FunctionalInterface註釋的接口,但是滿足函數式接口的約束。
函數式的約束:
- 接口有且只能有個一個抽象方法,只有方法定義,沒有方法體 。
- 在接口中覆寫Object類中的public方法,不算是函數式接口的方法。
- 在接口中的default方法,不算是函數式接口的方法。
- 在接口中的static方法,不算是函數式接口的方法。
常用函數式接口
Consumer<T>:消費型接口
代表了接受一個輸入參數並且無返回的操作
Consumer<String> testConsumer = param -> System.out.println(param);
testConsumer.accept("testConsumer");
Consumer默認提供了andThen
作用:用於連接兩個Consumer接口,一個是調用andThen方法的Consumer接口this,一個是andThen方法的參數after。
con1.andThen(con2).accept(s);
等價於con1.accept(s);con2.accept(s);
Supplier<T>:供給型接口
無參數,返回一個結果。
Supplier<String> testSupplier = () -> String.valueOf(Math.random());
System.out.println(testSupplier.get());
Function<T, R>:函數型接口
接受一個輸入參數,返回一個結果
Function<Integer, Integer> testFunction = s -> s * 2;
System.out.println(testFunction.apply(6));
默認提供的一些方法
Predicate<T>:斷言型接口
接受一個輸入參數,返回一個布爾值結果。
Predicate<String> testPredicate = s -> s.equals("test");
System.out.println(testPredicate.test("test"));
默認提供的一些方法
注意:
- and方法與邏輯運算符&&功能相同
- or方法與邏輯運算符||功能相同
- negate方法與邏輯運算符!功能相同
每種接口都有一些默認的方法,可以根據業務需求組合出更多的效果
擴展函數接口
參數個數上擴展:
比如接收雙參數的,有 Bi 前綴, 比如 BiConsumer<T,U>, BiFunction<T,U,R> ;
特殊常用的變形:
比如 BinaryOperator , 是同類型的雙參數 BiFunction<T,T,T> ,二元操作符 ; UnaryOperator 是 Function<T,T> 一元操作符。
類型上擴展:
比如接收原子類型參數的,比如 [Int|Double|Long] [Function|Consumer|Supplier|Predicate]
爲什麼要有基本類型擴展
只有對象類型才能作爲泛型參數,對於基本類型就涉及到裝箱拆箱的操作,雖然是自動的
但是這不可避免給內存帶來了額外的開銷,裝箱和拆箱都會帶來開銷
所以爲了減小這些性能開銷 對基本類型進行類型擴展
Stream 類的某些方法對基本類型和裝箱類型做了區分
Java 8中,僅對 整型、長整型和雙浮點型做了特殊處理 因爲它們在數值計算中用得最多
對基本類型做特殊處理的方法在命名上有明確的規範
- 如果參數是基本類型,則不加前綴只需類型名即可
- 如果方法返回類型爲基本類型,則在基本類型前再加上一個 To
總結一句話:加了類型前綴[Int|Double|Long] 表示參數是基本類型, 如果在此基礎上又加上了To 表示返回類型是基本類型
如有可能,應儘可能多地使用對基本類型做過特殊處理的方法,進而改善性能
其它
方法引用
方法引用可以在某些條件成立的情況下,更加簡化lambda表達式的聲明。方法引用語法格式有以下三種:
- objectName::instanceMethod
- ClassName::staticMethod
- ClassName::instanceMethod
前兩種方式類似,等同於把 lambda 表達式的參數直接當成 instanceMethod/staticMethod 的參數來調用。比如 System.out::println 等同於 x->System.out.println(x);Math::max 等同於 (x, y)->Math.max(x,y) 。
最後一種方式,等同於把lambda表達式的第一個參數當成 instanceMethod 的目標對象,其他剩餘參數當成該方法的參數。比如 String::toLowerCase 等同於 x -> x.toLowerCase()。
//Funciton Lambda表達式: (Apple a) -> a.getWeight() 等價的方法引用: Apple::getWeight //Conusmer Lambda表達式: () -> Thread.currentThread().dumpStack() 等價的方法引用: Thread.currentThread()::dumpStack //BiFunction Lambda表達式: (str, i) -> str.substring(i) 等價的方法引用: String::substring //Function Lambda表達式: (String s) -> System.out.println(s) 等價的方法引用: System.out::printl
只有當lambda表達式的體只調用一個方法而不做其他操作時,纔可以把lambda表達式重寫爲方法引用。如以下表達式:
s -> s.length == 0
這裏有一個方法調用,但是還有一個比較,因而這裏不能使用方法引用。
構造器引用
構造器引用語法如下:ClassName::new,把lambda表達式的參數當成ClassName構造器的參數 。例如BigDecimal::new等同於x->new BigDecimal(x)。3
//無參構造函數 Supplier<Apple> c1 = () -> new Apple(); Supplier<Apple> c1 = Apple::new; //一元構造函數 Function<Integer, Apple> c2 = (weight) -> new Apple(weight); Function<Integer, Apple> c2 = Apple::new; //二元構造函數 BiFunction<Integer, String, Apple> c3 =(weight, color) -> new Apple(weight, color); BiFunction<Integer, String, Apple> c3 = Apple::new;
Lambda表達式和匿名內部類的區別
一、所需類型不同:
- 匿名內部類可以是:接口、抽象類、或者具體類
- Lambda 表達式只能是:接口
二、使用限制不同:
- 匿名內部類:接口中可以多個或者一個方法
- Lambda 表達式要求:接口中的只能有一個方法
三、實現原理不同:
查看項目文件夾中的時候可以看到
- 匿名內部類:編譯之後會產生單獨的 .class 文件
- Lambda 表達式:編譯之後不會有單獨的 .class 文件出現,對應的字節碼會在運行的時候動態生成。
轉載
https://blog.csdn.net/wanghao112956/article/details/91865095
https://www.cnblogs.com/dgwblog/p/11739500.html
https://cloud.tencent.com/developer/article/1333532
https://www.cnblogs.com/hellovan/p/13528201.html