Java8 Lambda FP Functional Programing

Java8 - Lambda語法

林政儀 2017/12/04 13:34:42
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",因此語法概念就是"省略不必要的字",因此:
  1. Class省略
  2. method省略
  3. 變數型態省略

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);
林政儀