一文徹底掌握J(rèn)ava中的Lambda表達(dá)式
為什么需要 Lambda 表達(dá)式?
Lambda表達(dá)式是Java函數(shù)式編程的核心概念之一。同時(shí)它也是一個(gè)相對(duì)難以理解的概念。
那么為什么需要它呢?
Lambda 表達(dá)式的出現(xiàn)是為了簡(jiǎn)化實(shí)現(xiàn)函數(shù)式接口 Functional Interface 的,什么又是函數(shù)式接口呢?
因此再講函數(shù)式接口之前,先來(lái)回顧一下 interface 的概念與基本使用。
interface 接口

接口是用來(lái)定義一個(gè)協(xié)議或約定,它只聲明方法但不提供方法的具體實(shí)現(xiàn)。
我們稱(chēng)這樣的方法為抽象方法 Abstract Method 。
具體方法的實(shí)現(xiàn)是由實(shí)現(xiàn)這個(gè)接口的 class 來(lái)提供的:

這樣一來(lái)將接口的聲明與具體實(shí)現(xiàn)分開(kāi),業(yè)務(wù)中就只需要關(guān)心方法,而不需要關(guān)心具體的實(shí)現(xiàn)類(lèi),從而實(shí)現(xiàn)代碼的解耦與模塊化。
假如現(xiàn)在有一個(gè)需求,通過(guò)一個(gè)方法來(lái)發(fā)送多種不同類(lèi)型的消息,比如既可以發(fā)送 Email 消息也可以發(fā)送 SMS 短信消息,這種場(chǎng)景就非常適合使用 Interface 來(lái)實(shí)現(xiàn)。
我們先定義一個(gè)消息發(fā)送的接口:
/**
* @author TheSea
* @description 消息接口
* @createDate 2025/12/28
*/
public interface Message {
void send();
}接下來(lái)創(chuàng)建一個(gè) Email 類(lèi)來(lái)實(shí)現(xiàn)這個(gè) Message 接口:
/**
* @author TheSea
* @description 郵件消息類(lèi)
* @createDate 2025/12/28
*/
public class Email implements Message{
private String email;
@Override
public void send() {
System.out.println("這是一份郵件消息.");
}
}
再來(lái)創(chuàng)建一個(gè) Sms 類(lèi)來(lái)實(shí)現(xiàn)這個(gè) Message 接口:
/**
* @author TheSea
* @description 短信消息類(lèi)
* @createDate 2025/12/28
*/
public class Sms implements Message{
private String phone;
@Override
public void send() {
System.out.println("這是一份手機(jī)短信消息.");
}
}
現(xiàn)在我們來(lái)發(fā)送郵件消息,寫(xiě)一個(gè)方法用來(lái)負(fù)責(zé)統(tǒng)一發(fā)送消息:
// 參數(shù)類(lèi)型為接口,因?yàn)閰?shù)類(lèi)型為 Message 接口類(lèi)型,
// 這就意味著不管傳入的是 Email 還是 Sms 對(duì)象,
// 它們的類(lèi)都已經(jīng)各自實(shí)現(xiàn)了 Message 接口的 send 方法
// 所以 sendMsg 方法內(nèi)部只需要執(zhí)行 message 的 send 方法
// 而不需要關(guān)心執(zhí)行的到底是哪個(gè)對(duì)象的 send 方法
// 免去了復(fù)雜的判斷和切換,這就是面向接口編程的一個(gè)典型應(yīng)用場(chǎng)景
public static void sendMsg(Message message){
message.send();
}
現(xiàn)在嘗試發(fā)送郵件消息:
public class Main {
public static void main(String[] args) {
//發(fā)送郵件消息
Message emailMessage = new Email();
sendMsg(emailMessage);
}
// 參數(shù)類(lèi)型為接口,因?yàn)閰?shù)類(lèi)型為 Message 接口類(lèi)型,
// 這就意味著不管傳入的是 Email 還是 Sms 對(duì)象,
// 它們的類(lèi)都已經(jīng)各自實(shí)現(xiàn)了 Message 接口的 send 方法
// 所以 sendMsg 方法內(nèi)部只需要執(zhí)行 message 的 send 方法
// 而不需要關(guān)心執(zhí)行的到底是哪個(gè)對(duì)象的 send 方法
// 免去了復(fù)雜的判斷和切換,這就是面向接口編程的一個(gè)典型應(yīng)用場(chǎng)景
public static void sendMsg(Message message){
message.send();
}
}

再來(lái)嘗試一下發(fā)送 Sms 消息:
public class Main {
public static void main(String[] args) {
//發(fā)送短信消息
Message smsMessage = new Sms();
sendMsg(smsMessage);
}
// 參數(shù)類(lèi)型為接口,因?yàn)閰?shù)類(lèi)型為 Message 接口類(lèi)型,
// 這就意味著不管傳入的是 Email 還是 Sms 對(duì)象,
// 它們的類(lèi)都已經(jīng)各自實(shí)現(xiàn)了 Message 接口的 send 方法
// 所以 sendMsg 方法內(nèi)部只需要執(zhí)行 message 的 send 方法
// 而不需要關(guān)心執(zhí)行的到底是哪個(gè)對(duì)象的 send 方法
// 免去了復(fù)雜的判斷和切換,這就是面向接口編程的一個(gè)典型應(yīng)用場(chǎng)景
public static void sendMsg(Message message){
message.send();
}
}

到這里按照傳統(tǒng)方法,我們已經(jīng)正確的實(shí)現(xiàn)和使用了接口,應(yīng)用場(chǎng)景也 OK。
問(wèn)題的引入
但是我們會(huì)發(fā)現(xiàn),要發(fā)送一條消息要經(jīng)過(guò)好多個(gè)步驟,比如我們發(fā)送 Sms 消息。
我們需要先創(chuàng)建一個(gè) Sms 類(lèi),然后去實(shí)現(xiàn) Message 接口中的 send() 方法,然后再實(shí)例化一個(gè) Sms 對(duì)象,再調(diào)用 send 發(fā)送消息,非常繁復(fù)。
而 Lambda 表達(dá)式的出現(xiàn)就可以幫助我們解決這個(gè)問(wèn)題。
Lambda 表達(dá)式
Lambda 表達(dá)式可以提供一種簡(jiǎn)單快速的方式來(lái)實(shí)現(xiàn)上述接口,接口的抽象方法的具體實(shí)現(xiàn)直接在 Lambda 表達(dá)式里面定義即可,而不用像傳統(tǒng)方法那樣創(chuàng)建類(lèi)然后實(shí)現(xiàn)接口的抽象方法、實(shí)例化對(duì)象最后調(diào)用方法,大大簡(jiǎn)化了代碼的編寫(xiě)流程。
接下來(lái)用 Lambda 表達(dá)式重新實(shí)現(xiàn)一下剛才的例子。
public class Main {
public static void main(String[] args) {
// send()方法的實(shí)現(xiàn):
// public void send() {
// System.out.println("這是一份郵件消息.");
// }
// Lambda 表達(dá)式可以直接可以直接實(shí)現(xiàn) Message 接口的 send 抽象方法,然后作為參數(shù)傳給 sendMessage
// () 代表的就是 send() 方法后面的括號(hào),即參數(shù)列表
// {} 大括號(hào)里面的內(nèi)容就是 send() 方法的函數(shù)體
sendMsg(() ->{
System.out.println("這是一份郵件消息.");
});
}
// 參數(shù)類(lèi)型為接口,因?yàn)閰?shù)類(lèi)型為 Message 接口類(lèi)型,
// 這就意味著不管傳入的是 Email 還是 Sms 對(duì)象,
// 它們的類(lèi)都已經(jīng)各自實(shí)現(xiàn)了 Message 接口的 send 方法
// 所以 sendMsg 方法內(nèi)部只需要執(zhí)行 message 的 send 方法
// 而不需要關(guān)心執(zhí)行的到底是哪個(gè)對(duì)象的 send 方法
// 免去了復(fù)雜的判斷和切換,這就是面向接口編程的一個(gè)典型應(yīng)用場(chǎng)景
public static void sendMsg(Message message){
message.send();
}
}
上面這條 Lambda 表達(dá)式就等效于之前用傳統(tǒng)方法實(shí)現(xiàn)的 Email 實(shí)例對(duì)象。

如果 Lambda 的函數(shù)體里只包含一個(gè)表達(dá)式,那么可以省略大括號(hào)進(jìn)行簡(jiǎn)寫(xiě):
sendMsg(() -> System.out.println("這是一份郵件消息."));
如果 Lambda 表達(dá)式包含多條語(yǔ)句或者需要明確的流程控制,那么就必須要使用大括號(hào)。
目前的例子中抽象方法并沒(méi)有參數(shù),現(xiàn)在假設(shè)抽象方法有一個(gè)參數(shù) name:
public interface Message {
void send(String name);
}
那么 Lambda 表達(dá)式中就需要這么定義:
public class Main {
public static void main(String[] args) {
sendMsg((name) -> System.out.println(name + ": 這是一份郵件消息."));
}
public static void sendMsg(Message message){
message.send("TheSea");
}
}

注意上面的 參數(shù)名 是形參,可以隨便寫(xiě),不一定要和接口中聲明的一樣,比如這樣寫(xiě)也是一樣的效果:
sendMsg((nameInLambda) -> System.out.println(nameInLambda + ": 這是一份郵件消息."));
如果參數(shù)只有一個(gè)的話(huà),Lambda 表達(dá)式的參數(shù)列表的小括號(hào)是可以去掉的:
sendMsg(name -> System.out.println(name + ": 這是一份郵件消息."));
那么如果有多個(gè)參數(shù)呢?那就按順序書(shū)寫(xiě),然后中間用逗號(hào)分隔即可,注意必須要帶上小括號(hào)嗷:
public interface Message {
void send(String name, int age);
}
public class Main {
public static void main(String[] args) {
sendMsg((name, age) -> System.out.println(name + ": 您" + age + "歲了,這是一份郵件消息."));
}
public static void sendMsg(Message message) {
message.send("TheSea", 18);
}
}

注意一定要參數(shù)的聲明順序書(shū)寫(xiě)編譯器才能對(duì)上號(hào),否則的話(huà)會(huì)出現(xiàn)下面的荒唐局面:
public class Main {
public static void main(String[] args) {
sendMsg((age, name) -> System.out.println(name + ": 您" + age + "歲了,這是一份郵件消息."));
}
public static void sendMsg(Message message) {
message.send("TheSea", 18);
}
}

以上示例的抽象方法都沒(méi)有返回值,我們來(lái)看一下有返回值的情況:
public interface Message {
String send(String name, int age);
}
此時(shí)我們之前的 Lambda 表達(dá)式就會(huì)報(bào)錯(cuò):

因?yàn)槲覀冃枰蟹祷刂担?/p>

可以看到,如果函數(shù)體只有一條返回語(yǔ)句,像上面這樣寫(xiě)會(huì)報(bào)錯(cuò),原因是如果只有一條返回語(yǔ)句的話(huà),return 關(guān)鍵字是不需要存在的,可以簡(jiǎn)化成下面這樣:
public class Main {
public static void main(String[] args) {
sendMsg((age, name) -> "Lambda 表達(dá)式的返回值");
}
public static void sendMsg(Message message) {
String res = message.send("TheSea", 18);
System.out.println(res);
}
}
現(xiàn)在把參數(shù)應(yīng)用進(jìn)去,當(dāng)函數(shù)體有多條表達(dá)式的時(shí)候,需要用大括號(hào)包圍:
public class Main {
public static void main(String[] args) {
sendMsg((name, age) -> {
System.out.println(name + ": 您" + age + "歲了,這是一份郵件消息.");
return "Lambda 表達(dá)式的返回值";
});
}
public static void sendMsg(Message message) {
String res = message.send("TheSea", 18);
System.out.println(res);
}
}

另外如果現(xiàn)在我們想改另外一種發(fā)送方式,比如使用 Sms,那么我們現(xiàn)在也不需要再新建一個(gè) Sms 類(lèi)來(lái)實(shí)現(xiàn)接口了,直接修改 Lambda 表達(dá)式的函數(shù)體就可以:
public class Main {
public static void main(String[] args) {
sendMsg((name, age) -> {
System.out.println(name + ": 您" + age + "歲了,這是一份短信消息.");
return "Lambda 表達(dá)式的返回值";
});
}
public static void sendMsg(Message message) {
String res = message.send("TheSea", 18);
System.out.println(res);
}
}

Lambda 表達(dá)式也可以像普通對(duì)象那樣賦值給變量,如下:
public class Main {
public static void main(String[] args) {
Message lambda = (name, age) -> {
System.out.println(name + ": 您" + age + "歲了,這是一份短信消息.");
return "Lambda 表達(dá)式的返回值";
};
sendMsg(lambda);
}
public static void sendMsg(Message message) {
String res = message.send("TheSea", 18);
System.out.println(res);
}
}
綜上所述,Lambda 讓我們實(shí)現(xiàn)接口抽象方法的整個(gè)過(guò)程變得非常簡(jiǎn)單。
那么 Lambda 表達(dá)式是不是可以應(yīng)用在任何形式的 interface 上呢?
當(dāng)然不行!
Lambda 表達(dá)式只能應(yīng)用于有且只有一個(gè)抽象方法的接口上,我們稱(chēng)這樣的接口為函數(shù)式接口。
那為什么Java要這么定義呢?
因?yàn)橐粋€(gè)接口如果有多個(gè)抽象方法的話(huà),那么在使用 Lambda 表達(dá)式的時(shí)候 Java 將無(wú)法確定 Lambda 試圖實(shí)現(xiàn)哪一個(gè)抽象方法,而當(dāng)只有一個(gè)抽象方法的時(shí)候?qū)τ?Java 來(lái)說(shuō)意圖就非常明確。
因此只有函數(shù)式接口才可以使用 Lambda 表達(dá)式。
另外,我們可以使用 @FunctionalInterface 注解來(lái)標(biāo)識(shí)該接口為一個(gè)函數(shù)式接口:
@FunctionalInterface
public interface Message {
String send(String name, int age);
}
這個(gè)注解是可選的,因?yàn)楹瘮?shù)式接口定義的本身并不依賴(lài)于這個(gè)注解,即便不標(biāo)注但只要符合函數(shù)式接口的定義條件,Java自動(dòng)就會(huì)把它當(dāng)成是函數(shù)式接口。
不過(guò)工程開(kāi)發(fā)中我們還是建議使用該注解來(lái)進(jìn)行標(biāo)識(shí),這樣的話(huà)如果出現(xiàn)多余一個(gè)抽象方法或者是沒(méi)有抽象方法的時(shí)候就會(huì)編譯報(bào)錯(cuò),同時(shí)也會(huì)提供一個(gè)非常好的語(yǔ)義方便工程師更好的理解該接口的設(shè)計(jì)意圖。
總結(jié)
Lambda 表達(dá)式歸根到底是一種語(yǔ)法糖,用于簡(jiǎn)化函數(shù)式接口的實(shí)現(xiàn)。
在 Java 的函數(shù)式編程中起到了關(guān)鍵的作用,Java 標(biāo)準(zhǔn)庫(kù)中也包含了非常多的內(nèi)置的函數(shù)式接口,比如 Predicate、Function、Consumer、Supplier 等等,經(jīng)常與 Stream API 結(jié)合使用。
所以掌握 Lambda 表達(dá)式是非常重要的。
到此這篇關(guān)于Java中Lambda表達(dá)式的文章就介紹到這了,更多相關(guān)Java中Lambda表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java Lambda表達(dá)式超詳細(xì)介紹
- Java編程中使用lambda表達(dá)式的奇技淫巧
- Java Lambda表達(dá)式詳解和實(shí)例
- java lambda表達(dá)式用法總結(jié)
- Java8新特性之Lambda表達(dá)式淺析
- Java Lambda表達(dá)式原理及多線(xiàn)程實(shí)現(xiàn)
- Java8中l(wèi)ambda表達(dá)式的應(yīng)用及一些泛型相關(guān)知識(shí)
- 詳解Java中的Lambda表達(dá)式
- 詳解Java中的Lambda表達(dá)式
- java中l(wèi)ambda表達(dá)式語(yǔ)法說(shuō)明
相關(guān)文章
使用IDEA搭建SSM框架的詳細(xì)教程(spring + springMVC +MyBatis)
這篇文章主要介紹了使用IDEA搭建SSM框架的詳細(xì)教程 spring + springMVC +MyBatis,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
Javamelody監(jiān)控不到sql的問(wèn)題(親測(cè)有效)??
JavaMelody是用來(lái)在QA和實(shí)際運(yùn)行生產(chǎn)環(huán)境中監(jiān)控Java或Java?EE應(yīng)用程序服務(wù)器的一個(gè)開(kāi)源框架,這篇文章主要介紹了Javamelody監(jiān)控不到sql(親測(cè)有效)??,需要的朋友可以參考下2022-10-10
JavaWeb實(shí)現(xiàn)顯示mysql數(shù)據(jù)庫(kù)數(shù)據(jù)
MySQL是最流行的關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),在WEB應(yīng)用方面MySQL是最好的。本文將利用JavaWeb實(shí)現(xiàn)顯示mysql數(shù)據(jù)庫(kù)數(shù)據(jù)功能,需要的可以參考一下2022-03-03
SpringMVC下實(shí)現(xiàn)Excel文件上傳下載
這篇文章主要為大家詳細(xì)介紹了SpringMVC下實(shí)現(xiàn)Excel文件上傳下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
淺談Java中生產(chǎn)者與消費(fèi)者問(wèn)題的演變
這篇文章主要介紹了淺談Java中生產(chǎn)者與消費(fèi)者問(wèn)題的演變,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10
各種格式的編碼解碼工具類(lèi)分享(hex解碼 base64編碼)
這篇文章主要介紹了各種格式的編碼解碼工具類(lèi),集成Commons-Codec、Commons-Lang及JDK提供的編解碼方法2014-01-01

