Lambda 表達(dá)式 VS 匿名內(nèi)部類
本節(jié)我們將重點(diǎn)討論下 Lambda 表達(dá)式與匿名內(nèi)部類之間除了語(yǔ)法外還有哪些差別。再開(kāi)始講解之前我們先列出兩者重要的兩點(diǎn)區(qū)別:
- 無(wú)標(biāo)識(shí)性: 內(nèi)部類會(huì)確保創(chuàng)建一個(gè)擁有唯一表示的對(duì)象,而 Lambda 表達(dá)式的計(jì)算結(jié)果有可能有唯一標(biāo)識(shí),也可能沒(méi)有。
- 作用域規(guī)則: 由于內(nèi)部類可以從父類繼承屬性,Lambda 表達(dá)式卻不能,所以,內(nèi)部類的作用域規(guī)則比 Lambda 表達(dá)式要復(fù)雜。(關(guān)于 Lambda 表達(dá)式的作用域規(guī)則可以回看 03 節(jié)的內(nèi)容)
我們來(lái)看一個(gè)例子:
public class TestLambdaAndInnerClass {
public void test(){
//匿名類實(shí)現(xiàn)
Runnable innerRunnable = new Runnable(){
@Override
public void run() {
System.out.println("call run in innerRunnable:\t"+this.getClass());
}
};
//Lambda表達(dá)式實(shí)現(xiàn)
Runnable lambdaRunnable = () -> System.out.println("call run in lambdaRunnable:\t"+this.getClass());
new Thread(innerRunnable).start();
new Thread(lambdaRunnable).start();
}
public static void main(String...s){
new TestLambdaAndInnerClass().test();
}
}
返回結(jié)果為:
call run in innerRunnable: class com.github.x19990416.item.TestLambdaAndInnerClass$1
call run in lambdaRunnable: class com.github.x19990416.item.TestLambdaAndInnerClass
上面的例子分別在內(nèi)部類和 Lambda 表達(dá)式中調(diào)用各自的 this
指針。我們發(fā)現(xiàn) Lambda 表達(dá)式的 this
指針指向的是其外圍類 TestLambdaAndInnerClass
,匿名內(nèi)部類的指針指向的是其本身。(對(duì)于 super
也是一樣的結(jié)果)
我們來(lái)看下編譯出來(lái)的文件:
?
?
其中 TestLambdaAndInnerClass$1.class
是匿名類 innerRunnable
編譯出來(lái)的 class 文件,對(duì)于 Lambda 表達(dá)式 lambdaRunnable
則沒(méi)有編譯出具體的 class 文件。
這說(shuō)明對(duì)于 Lambda 表達(dá)式而言,編譯器并不認(rèn)為它是一個(gè)完全的類(或者說(shuō)它是一個(gè)特殊的類對(duì)象),所以也不具備一個(gè)完全類的特征。
Tips: 匿名類的
this
、super
指針指向的是其自身的實(shí)例,而 Lambda 表達(dá)式的this
、super
指針指向的是創(chuàng)建這個(gè) Lambda 表達(dá)式的類對(duì)象的實(shí)例。
1. 無(wú)標(biāo)識(shí)性問(wèn)題
在 Lambda 表達(dá)式出現(xiàn)之前,一個(gè) Java 程序的行為總是與對(duì)象關(guān)聯(lián),以標(biāo)識(shí)、狀態(tài)和行為為特征。然而 Lambda 表達(dá)式則違背了這個(gè)規(guī)則。
雖然 Lambda 表達(dá)式可以共享對(duì)象的一些屬性,但是表示行為是其唯一的用處。由于沒(méi)有狀態(tài),所以表示問(wèn)題也就不那么重要了。在 Java 語(yǔ)言的規(guī)范中對(duì) Lambda 表達(dá)式唯一的要求就是必須計(jì)算出其實(shí)現(xiàn)的相當(dāng)?shù)暮瘮?shù)接口的實(shí)例類。如果 Java 對(duì)每個(gè) Lambda 表達(dá)式都擁有唯一的表示,那么 Java 就沒(méi)有足夠的靈活性來(lái)對(duì)系統(tǒng)進(jìn)行優(yōu)化。
2. Lambda 表達(dá)式的作用域規(guī)則
匿名內(nèi)部類與大多數(shù)類一樣,由于它可以引用從父類繼承下來(lái)的名字,以及聲明在外部類中的名字,所以它的作用域規(guī)則非常復(fù)雜。
Lambda 表達(dá)式由于不會(huì)從父類型中繼承名字,所以它的作用于規(guī)則要簡(jiǎn)單很多。除了參數(shù)以外,用在 Lambda 表達(dá)式中的名字的含義與表達(dá)式外面是一樣的。
由于 Lambda 表達(dá)式的聲明就是一個(gè)語(yǔ)句塊,所以 this
與 super
與表達(dá)式外圍環(huán)境的含義一樣,換言之它們指向的是外圍對(duì)象的父類對(duì)象。
3. 小結(jié)
本節(jié)我們主要討論了 Lambda 表達(dá)式與匿名內(nèi)部類的本質(zhì)區(qū)別,其中重要的是要記住 this
和 super
在兩者之間的作用范圍。記住這個(gè)作用范圍可以更好的幫助我們理解 Lambda 表達(dá)式的作用域,避免我們?cè)谑褂?Lambda 表達(dá)式中由于作用域引起的 bug,這一類的 bug 在實(shí)際中定位是非常困難的。

?
在 Java 里面,所有的方法參數(shù)都是有固定類型的,比如將數(shù)字 9 作為參數(shù)傳遞給一個(gè)方法,它的類型是 int;字符串 “9” 作為參數(shù)傳遞給方法,它的類型是 String。那么 Lambda 表達(dá)式的類型由是什么呢?通過(guò)本節(jié)我們學(xué)習(xí)什么是函數(shù)式接口,它與 Lambda 表達(dá)式的關(guān)系。