每日總結4
Lambda 表示式
Lambda 表示式是一個匿名函式,Lambda表示式基於數學中的λ演算得名,直接對應於其中的lambda抽象,是一個匿名函式,即沒有函式名的函式。Lambda表示式可以表示閉包。
在 Java 中,Lambda 表示式的格式是像下面這樣.
// 無引數,無返回值
() -> log.info("Lambda")
// 有引數,有返回值
(int a, int b) -> { a+b }
其等價於
log.info("Lambda");
private int plus(int a, int b){
return a+b;
}
最常見的一個例子就是新建執行緒,有時候為了省事,會用下面的方法建立並啟動一個執行緒,這是匿名內部類的寫法,new Thread需要一個 implements 自Runnable型別的物件例項作為引數,比較好的方式是建立一個新類,這個類 implements Runnable,然後 new 出這個新類的例項作為引數傳給 Thread。而匿名內部類不用找物件接收,直接當做引數。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("快速新建並啟動一個執行緒");
}
}).start();
但是這樣寫是不是感覺看上去很亂、很土,而這時候,換上 Lambda 表示式就是另外一種感覺了。
new Thread(()->{
System.out.println("快速新建並啟動一個執行緒");
}).start();
怎麼樣,這樣一改,瞬間感覺清新脫俗了不少,簡潔優雅了不少。
Lambda 表示式簡化了匿名內部類的形式,可以達到同樣的效果,但是 Lambda 要優雅的多。雖然最終達到的目的是一樣的,但其實內部的實現原理卻不相同。
匿名內部類在編譯之後會建立一個新的匿名內部類出來,而 Lambda 是呼叫 JVM invokedynamic指令實現的,並不會產生新類。
方法引用
方法引用的出現,使得我們可以將一個方法賦給一個變數或者作為引數傳遞給另外一個方法。::雙冒號作為方法引用的符號,比如下面這兩行語句,引用 Integer類的 parseInt方法。
Function<String, Integer> s = Integer::parseInt;
Integer i = s.apply("10");
或者下面這兩行,引用 Integer類的 compare方法。
Comparator<Integer> comparator = Integer::compare;
int result = comparator.compare(100,10);
再比如,下面這兩行程式碼,同樣是引用 Integer類的 compare方法,但是返回型別卻不一樣,但卻都能正常執行,並正確返回。
IntBinaryOperator intBinaryOperator = Integer::compare;
int result = intBinaryOperator.applyAsInt(10,100);
相信有的同學看到這裡恐怕是下面這個狀態,完全不可理喻嗎,也太隨便了吧,返回給誰都能接盤。
先別激動,來來來,現在咱們就來解惑,解除蒙圈臉。
Q:什麼樣的方法可以被引用?
A:這麼說吧,任何你有辦法訪問到的方法都可以被引用。
Q:返回值到底是什麼型別?
A:這就問到點兒上了,上面又是 Function、又是Comparator、又是 IntBinaryOperator的,看上去好像沒有規律,其實不然。
返回的型別是 Java 8 專門定義的函式式介面,這類介面用 @FunctionalInterface 註解。
比如 Function這個函式式介面的定義如下:
@FunctionalInterface
public interfaceFunction<T, R> {
R apply(T t);
}
還有很關鍵的一點,你的引用方法的引數個數、型別,返回值型別要和函式式介面中的方法宣告一一對應才行。
比如 Integer.parseInt方法定義如下:
public static intparseInt(String s) throws NumberFormatException { return parseInt(s,10);
}
首先parseInt方法的引數個數是 1 個,而 Function中的 apply方法引數個數也是 1 個,引數個數對應上了,再來,apply方法的引數型別和返回型別是泛型型別,所以肯定能和 parseInt方法對應上。
這樣一來,就可以正確的接收Integer::parseInt的方法引用,並可以呼叫Funciton的apply方法,這時候,呼叫到的其實就是對應的 Integer.parseInt方法了。
用這套標準套到 Integer::compare方法上,就不難理解為什麼即可以用 Comparator<Integer>接收,又可以用 IntBinaryOperator接收了,而且呼叫它們各自的方法都能正確的返回結果。
Integer.compare方法定義如下:
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
返回值型別 int,兩個引數,並且引數型別都是 int。
然後來看Comparator和IntBinaryOperator它們兩個的函式式介面定義和其中對應的方法:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
@FunctionalInterface
public interface IntBinaryOperator {
int applyAsInt(int left, int right);
}
對不對,都能正確的匹配上,所以前面示例中用這兩個函式式介面都能正常接收。其實不止這兩個,只要是在某個函式式介面中聲明瞭這樣的方法:兩個引數,引數型別是 int或者泛型,並且返回值是 int或者泛型的,都可以完美接收。
JDK 中定義了很多函式式介面,主要在 java.util.function包下,還有 java.util.Comparator 專門用作定製比較器。另外,前面說的 Runnable也是一個函式式介面。