Java 目前已經出到13的版本,但是國內大部分公司應該都停留在 Java 8 的版本(不敢承擔升級帶來的風險)。在Java8中給我們帶來了 Lambda表達式和Stream流式操作,提供了函數式編程和簡化批處理操作。可能大家日常使用很多,但是很少去關注Lambda 和 Stream實現。本篇就帶着大家一起關注這兩塊知識,搬好小板凳我們一起看電視。
什么是Lambda
簡單來說,編程中提到的 lambda 表達式,通常是指需要一個函數,但是又不想費神去命名一個函數的場合下使用,也就是指匿名函數。
一般我們使用變量來進行賦值操作:
但是在 Java8 之前是沒有提供將一個方法賦值給一個變量的操作:
在 JavaScript 中有 閉包
的概念,所以 Java不甘落后,也去學習了人家,搞出了匿名函數的概念。
在Java 8里面,所有的Lambda的類型都是一個接口,而Lambda表達式本身,也就是”那段代碼“,需要是這個接口的實現。這是我認為理解Lambda的一個關鍵所在,簡而言之就是,Lambda表達式本身就是一個接口的實現。
基本語法:
(parameters) -> { expression or statements }
示例:
// 無參數, 返回1+2的結果
() -> 1+2;
// 接收一個參數(數字類型),返回其2倍的值
x -> 2 * x;
// 接收2個參數(數字),返回表達式運算的結果
(x, y) -> x + y;
// 多個語句要用大括號包裹, 並且返回值要用return指明
(x, y) -> {
int result = x + y;
System.out.print(result);
return result;
};
Lambda表達式有如下約定:
- 一個 Lambda 表達式可以有零個或多個參數;
- 參數的類型既可以明確聲明,也可以根據上下文來推斷。例如:
(int a)
與(a)
效果相同; - 所有參數需包含在圓括號內,參數之間用逗號相隔。例如:
(a, b)
或(int a, int b)
或(String a, int b, float c);
- 空圓括號代表參數集為空。例如:
() -> 42;
- 當只有一個參數,且其類型可推導時,圓括號()可省略。例如:
a -> return a*a;
- Lambda 表達式的主體可包含零條或多條語句;
- 如果 Lambda 表達式的主體只有一條語句,花括號{}可省略。匿名函數的返回類型與該主體表達式一致;
- 如果 Lambda 表達式的主體包含一條以上語句,則表達式必須包含在花括號{}中(形成代碼塊)。匿名函數的返回類型與代碼塊的返回類型一致,若沒有返回則為空。
@FunctionalInterface 是 Java 8 新加入的一種接口,用於指明該接口類型聲明是根據 Java 語言規范定義的函數式接口。Java 8 還聲明了一些 Lambda 表達式可以使用的函數式接口,當你注釋的接口不是有效的函數式接口時,可以使用 @FunctionalInterface 解決編譯層面的錯誤。
根據定義,函數式接口只能有一個抽象方法,如果你嘗試添加第二個抽象方法,將拋出編譯時錯誤。例如:
@FunctionalInterface
public interface DoSomethingInterface {
void doSomeThing();
void doSomeThing1();
}
舉一個簡單使用的例子,首先定義一個接口:
@FunctionalInterface
public interface DoSomethingInterface<T> {
String doSomeThing(T t);
}
這個接口里面只能有一個函數,接口里是返回值為String類型的方法,那么下面使用它的時候就需要有返回值:
class MainTest{
public static void main(String[] args) {
DoSomethingInterface<String> t = str -> {
return str;
};
String print = t.doSomeThing("print");
System.out.println(print);
}
}
t就相當於是接口的具體實現,那么使用 t.doSomeThing("print")
就會去調用 t 的匿名函數執行。從這段代碼看,如果我們有一個抽象類的具體實現邏輯相對簡單不用大段代碼的時候,可以使用 Lambda 表達式去實現具體的邏輯,這樣就不用new 多個類。
Java 8 內置4大核心函數式接口
大家日常使用的List,Map,肯定大量使用了Stream流式處理,在流式處理中大量使用了Lambda,比如List的foreach方法就使用了Lambda表達式。Java8中抽象出來4類基本的函數式接口,日常開發中我們其實也是可以使用的。
函數式接口 | 參數類型 | 返回類型 | 用途 |
---|---|---|---|
Consumer
|
T | Void | void accept(T t) :將T 類型的參數應用於該方法 |
Supplier
|
T | T | T get():返回類型為T 的對象 |
Function<T,R> | T | R | R apply(T t):輸出T類型的參數返回R類型的結果 |
Predicate
|
T | Boolean | boolean test(T t): 確定類型為T 的參數是否滿足test邏輯 |
以上4種類型是Java默認提供的Lambda類,開箱即用。
Consumer使用:
public void test(){
save(user, t->{ saveUser(t); });
}
public void save(User user, Consumer<User> consumer){
consumer.accept(user);
}
Supplier使用:
public void test(){
Config config = getConfig(t -> {
return getAllConfig(t);
});
}
public Config getConfig(Supplier<Config> supplier) {
return supplier.get();
}
Function使用:
public void test(){
User user = getUser(t -> { return getUserByName(t); });
}
public User getUser(Function<String, User> function) {
return function.apply(t);
}
Predicate使用:
public void test(){
boolean b = checkStatus(user, t -> {
return checkUserExist(t);
});
}
public boolean checkStatus(User user, Predicate<User> predicate) {
return predicate.test(user);
}
以上關於Lambda就介紹到這里,大家應該明白了它是干啥的了吧。閉包是不是也沒有這么難懂呢!聯想到List的foreach使用,大家有沒有沖動想自己實現一個Lambda在日常開發中炫一把(如果別人看不懂,會打你的)。