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

?
在 Java 里面,所有的方法參數都是有固定類型的,比如將數字 9 作為參數傳遞給一個方法,它的類型是 int;字符串 “9” 作為參數傳遞給方法,它的類型是 String。那么 Lambda 表達式的類型由是什么呢?通過本節我們學習什么是函數式接口,它與 Lambda 表達式的關系。