Java8
Lambda
FP
Functional Programing
Java8 - Lambda語法
2017/12/04 13:34:42
1
1489
Java8 - Lambda語法
| 簡介 |
2014年所更新的java8版本,其新增的功能為java帶來新的設計模式與效能優化,Lambda的引入是為了FP(Function Program)必要的功能,也因此修改了一些java根生蒂固的觀念。本篇介紹Lambda語法與相關的操作。 |
| 作者 |
林政儀 |
1.使用Lambda之前先了解Java8對Interface的新定義。
(A) interface定義修改
在java8之前介面不允許實作method存在(如右圖的static&default method),是因為這樣可以避免在C++上出現的多重繼承(鑽石繼承)的困境。
但如此一來想要加強介面時就會遭遇困難,因為介面一旦修改所有實作都必須實作它。這也是java8這次想要引入Lambda時遇到的困難,他們想要加強Collection相關介面,但要是毅然決然修改了Collection介面的話,從java1.2開始所有Collection實作全都都必須一併修改。
#28 允許static method存在
#37 允許default method存在
#75 調用interface的default method
乍看之下好像很美好,但這樣又回到老問題,如果說同時有兩個interface有相同的default method時怎麼辦?很簡單,因為有兩個實作編譯器不曉得該呼叫哪個實作,因此這時候就會要求妳實作該mehotd了。
如果說該加強功能本該是各實作需要各自處理的,那倒是沒話說,修改介面時各實作當然要去修改。但重點就在於今天這個強化
是基於Collection本身有的method來強化,且所有介面的實作需要完成的動作都一樣時,若是要求所有介面都實作相同的邏輯,也未免太費工了。因此Java8想要引入FP(Function Program)的前提下,增強既有的介面勢在必行,也導致interface定義的修改避免不了。
例如大家一定常遇到一個case,就是for each Map的Key/Vaule,在java8之前需要如左下圖的操作才可以同時取得Key/Vaule。在使用
Lambda操作(右下圖所示),是不是簡潔易懂呢?雖然我個人認為型態無法在第一時間判別,但如果命名與code的閱讀性佳的情況下,才比較不容易造成混淆。
(B) Functional Interface
在java世界裡有許多單一method的interface,他們通常會執行一項任務隨即結束,例如最常見的Thread與Runnable關係,如下圖。
這種使用匿名類的方式會有效率上的疑慮,就是該匿名類其實真的會是一個class,在每次調用時都會new一個instance出來,若這是一段頻繁被執行的code,那很有可能頻繁的觸發GC,導致jvm浪費效能。
Lambda的引入解決了這個問題,並增加了可讀性,因此
Lambda就是針對單一method的interface操作語法,而該interface就稱之為FunctionalInterface,並使用@FunctionalInterface標註之。@FunctionalInterface定義該interface只能&必定存在一個abstract mehotd。
2.Lambda語法說明
因為Lamdba所支援的操作為單一abstract mthod interface,意思是"我就是只處理這個method",因此語法概念就是"省略不必要的字",因此:
- Class省略
- method省略
- 變數型態省略
method參照
既然它所處理的是interface裡唯一一個method,那也就是可以把別人的mehotd也當作Lamdba語法丟來丟去,只要他們的回傳值型態與參數型態、數量皆相同就可以參照。它用兩個":"來表示取用instance的method
3.Java提供的FunctionalInterface(官方doc)
- 泛型參數
| 回傳值\參數 | () | (T t) | (T t, U u) |
|---|---|---|---|
| void | java.lang.Runnable void run(); |
Consumer<T> void accept(T t); |
BiConsumer<T,U> void accept(T t, U u); |
| Generics | Supplier<T> T get(); |
Function<T,R> R apply(T t); |
BiFunction<T,U,R> R apply(T t, U u); |
| boolean | BooleanSupplier boolean getAsBoolean(); |
Predicate<T> boolean test(T t); |
BiPredicate<T,U> boolean test(T t, U u); |
| double | DoubleSupplier double getAsDouble(); |
ToDoubleFunction<T> double applyAsDouble(T value); |
ToDoubleBiFunction<T,U> double applyAsDouble(T t, U u); |
| int | IntSupplier int getAsInt(); |
ToIntFunction<T> int applyAsInt(T value); |
ToIntBiFunction<T,U> int applyAsInt(T t, U u); |
| long | LongSupplier long getAsLong(); |
ToLongFunction<T> long applyAsLong(T value); |
ToLongBiFunction<T,U> long applyAsLong(T t, U u); |
- 基本參數型態*1
| 回傳值\參數 | (double value) | (int t) | (long t) |
|---|---|---|---|
| void | DoubleConsumer void accept(double value); |
IntConsumer void accept(int value); |
LongConsumer void accept(long value); |
| Generics | DoubleFunction<R> R apply(double value); |
IntFunction<R> R apply(int value); |
LongFunction<R> R apply(long value); |
| boolean | DoublePredicate boolean test(double value); |
IntPredicate boolean test(int value); |
LongPredicate boolean test(long value); |
| double | DoubleUnaryOperator double applyAsDouble(double operand); |
IntToDoubleFunction double applyAsDouble(int value); |
LongToDoubleFunction double applyAsDouble(long value); |
| int |
DoubleToIntFunction int applyAsInt(double value); |
IntUnaryOperator int applyAsInt(int operand); |
LongToIntFunction int applyAsInt(long value); |
| long | DoubleToLongFunction long applyAsLong(double value); |
IntToLongFunction long applyAsLong(int value); |
LongUnaryOperator long applyAsLong(long operand); |
- 基本型態參數*2
| 回傳值\參數 | (double left, double right) | (int left, int right) | (long left, long right) |
|---|---|---|---|
| double | DoubleBinaryOperator double applyAsDouble(double left, double right); |
||
| int | IntBinaryOperator int applyAsInt(int left, int right); |
||
| long | LongBinaryOperator long applyAsLong(long left, long right); |
- 泛型參數+基本型態參數
| 回傳值\參數 | (T t, int value) | (T t, double value) | (T t, long value) |
|---|---|---|---|
| void | ObjIntConsumer<T> void accept(T t, int value); |
ObjDoubleConsumer<T> void accept(T t, double value); |
ObjLongConsumer<T> void accept(T t, long value); |
