# Lambda表達式

Lambda表達式是JDK8引入的一個新特性,又名匿名函數或閉包,可以不用創建顯示的標識符(變量名,函數名,類名等)來書寫需要的代碼。
java作為一門面向對象的語言,對語法有著嚴格的要求,在java中”一切皆對象“。但是有時候隨著我們寫的代碼多了又發現寫java代碼有時候會顯得復雜。比如我們每次在寫代碼時都需創建一個類,用到該類時又要創建該類的實例。有時候我們僅僅只是需要一個功能就能實現我們想要的目的,而不想要去和任何對象綁定在一起。例如定義一個方法,在需要的時候直接使用該方法就行了,就跟C語言這門面向過程的函數定義一樣。在C++和python中是同時支持面向過程與面向對象的,不用在寫代碼的時候每次都需要創建一個類。
**直接使用方法的思想**就是一種函數式編程的思想。這里的“方法”和"函數"并沒有太大的區別,很多時候,“方法”這個名詞用在面向對象的編程語言中,而“函數”更多用在面向過程的編程語言中,例如C語言。“方法”和“函數”在抽象的層面上講,都是接受用戶的輸入,處理數據,最后產生相應的結果。
總之,JDK8中引入的Lambda表達式就是為了能夠讓我們在一些情況下書寫出更簡潔的代碼。下面舉例說明
## 使用Lambda表達式
### 復雜的代碼
在創建多線程的方法中,我們有以下方法來創建一個線程,即使用Runnable接口重寫run方法來實現
```java
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("這是一個匿名內部類");
}
});
thread.start();
//也可以
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("這是一個匿名內部類");
}
}).start();
```
### 簡潔的代碼
從上面可以發現,其實我們代碼只想執行 System.out.println("這是一個匿名內部類");這一條語句而已,然而每次創建一個線程時需要創建一個Runnable接口的實現對象,就是因為這是按照“面向對象”的編程思想實現的。如果使用Lambda表達式的方法的代碼就簡潔了許多。
```java
new Thread(()->{
System.out.println("這是一個匿名內部類");
}).start();
```
**Lambda語法**
Lambda表達式的基本語法如下
> () -> {}
1. 前面的一對小括號 **()** 即為run方法的參數,這里run方法本身就不用傳遞任何參數。
2. 中間的 -**>** 表示將前面的參數傳遞給后面{}中的代碼。
3. **{}** 即表示run方法執行的代碼體。
其標準的格式即為:
```java
(參數類型 參數名稱...) -> {
//方法執行的代碼
}
```
## Lambda表達式的省略
Lambda表達式在上述標準的格式下還能進一步的進行省略,可省略的情況如下
- 小括號內的參數類型可以省略。
java是強類型語言,在方法是不允許省略參數的類型的。但是在Lambda表達式中參數的類型是可以省略的,這是得益于java的推測機制,可以通過上下文推斷出該變量是什么類型。
- 如果小括號內**只有一個參數**,則可以將括號也給省略了;例如
```java
name -> {System.out.println(name);}
```
個人覺得這個和上面那個省略一起用程序會比較可讀一些,即在只有一個參數的情況下省略變量的類型和括號,有多個參數的情況下變量類型就不要省略了。
- 如果花括號內**有且僅有一條語句**,則無論是否有返回值,可將花括號,return關鍵字,語句的分號一起省略了;例如
```java
name -> System.out.println(name)
```
## 什么情況下使用Lambda表達式
雖說使用Lambda表達式可以讓代碼更加簡潔,但是并不能夠用在任意的場景下的。就像上面舉的例子,用來替換Runnable接口的匿名實現類。即在使用時仍然是要存在一個接口,并且該接口中只有一個方法,不然我們直接用個 **()** 怎么知道調用哪個方法呢?當然這里值得一提的是,匿名實現類在編譯的時候仍然會創建該類的.class文件。而Lambda表達式并不會,用反編譯的方法發現Lambda表達式被封裝成主類的一個私有方法,具體請看<a href='https://objcoding.com/2019/03/04/lambda/'>關于java Lambda這篇就夠了</a>.
同樣的,在JDK8中另外一個新特性函數性接口的概念可以和Lambda表達式很好的配合使用起來。
### 函數性接口
這種接口內部只有一個抽象方法待實現(可以有一些默認 default 的方法),例如Runnable接口就只有一個run()方法待實現。Comparator接口中只有一個compare()方法,其他的方法均用default修飾,或static修飾(除了繼承自Object的方法)。
該接口定義舉例
```java
@FunctionalInterface
public interface MyFunctionInterface {
public abstract void method();
}
```
**@FunctionalInterface** 注解可以在編譯時期將該接口看成是一個函數性接口,加上該注解后如果在該接口中定義了多個抽象方法,就會報錯,加上該注解也可以讓人很明了這是一個函數性接口。
用lambda實現該接口中的method()方法:
```java
public class Demo {
/*
* 定義一個方法,將自定義的接口作為參數進行傳遞
*/
public static void show(MyFunctionInterface myFunctionInterface) {
myFunctionInterface.method();
}
public static void main(String[] args) {
show(() -> {
System.out.println("使用lambda表達式重寫接口中的抽象方法!");
});
}
}
```
可見,使用Lambda表達式一般都是將一個函數性接口作為一個參數傳遞給一個方法(構造方法也行),然后使用Lambda表達式實現該接口對應的抽象方法。
關于函數性接口,java定義了一些常用的供我們配合Lambda使用,在java.util.function包中。
## 小結
1. 當匿名類為函數式接口時,可以考慮用Lambda表達式簡化代碼的書寫;
2. 雖說Lambda表達式體現了函數式編程思想,但是java還是嚴格的使用面向對象的思想,所以Lambda表達式并不能任意供我們使用;
3. Lambda表達式的出現是java向 “寫更少,做更多” 邁進的巨大一步。
- 第一章 Java基礎
- ThreadLocal
- Java異常體系
- Java集合框架
- List接口及其實現類
- Queue接口及其實現類
- Set接口及其實現類
- Map接口及其實現類
- JDK1.8新特性
- Lambda表達式
- 常用函數式接口
- stream流
- 面試
- 第二章 Java虛擬機
- 第一節、運行時數據區
- 第二節、垃圾回收
- 第三節、類加載機制
- 第四節、類文件與字節碼指令
- 第五節、語法糖
- 第六節、運行期優化
- 面試常見問題
- 第三章 并發編程
- 第一節、Java中的線程
- 第二節、Java中的鎖
- 第三節、線程池
- 第四節、并發工具類
- AQS
- 第四章 網絡編程
- WebSocket協議
- Netty
- Netty入門
- Netty-自定義協議
- 面試題
- IO
- 網絡IO模型
- 第五章 操作系統
- IO
- 文件系統的相關概念
- Java幾種文件讀寫方式性能對比
- Socket
- 內存管理
- 進程、線程、協程
- IO模型的演化過程
- 第六章 計算機網絡
- 第七章 消息隊列
- RabbitMQ
- 第八章 開發框架
- Spring
- Spring事務
- Spring MVC
- Spring Boot
- Mybatis
- Mybatis-Plus
- Shiro
- 第九章 數據庫
- Mysql
- Mysql中的索引
- Mysql中的鎖
- 面試常見問題
- Mysql中的日志
- InnoDB存儲引擎
- 事務
- Redis
- redis的數據類型
- redis數據結構
- Redis主從復制
- 哨兵模式
- 面試題
- Spring Boot整合Lettuce+Redisson實現布隆過濾器
- 集群
- Redis網絡IO模型
- 第十章 設計模式
- 設計模式-七大原則
- 設計模式-單例模式
- 設計模式-備忘錄模式
- 設計模式-原型模式
- 設計模式-責任鏈模式
- 設計模式-過濾模式
- 設計模式-觀察者模式
- 設計模式-工廠方法模式
- 設計模式-抽象工廠模式
- 設計模式-代理模式
- 第十一章 后端開發常用工具、庫
- Docker
- Docker安裝Mysql
- 第十二章 中間件
- ZooKeeper