java8 - Lambda表達式

1.先了解下什麼是Lambda表達式

  • Lambda表達式可以理解爲可傳遞的匿名函數的一種方式:它沒有名稱,但它有參數列表、函數主體、返回類型,可能還有一個可以拋出的異常列表。
  • Lambda表達式可以作爲參數傳遞給方法或存儲在變量中 Lambda表達式是簡潔的

2.Lambda表達式的組成

lambda參數、->、lambda主體
示例:(int x,int y)-> {return x+y;}

3.Lambda基本語法

(parameters) -> expression 或
(parameters) -> { statements; }

4.Lambda如何使用

函數式接口:只定義一個抽象方法的接口

    有一個注意的點:在java8中接口可以有默認的實現(即在類沒有對方法進行實現時,其主體爲方法提供默認實現的方法),除了默認的方法,抽象方法有且只有一個才能算函數式接口

    Lambda表達式允許你直接內聯,爲函數式接口的抽象方法提供實現,並且將整個表達式作爲函數式接口的一個實例。

函數描述符:函數式接口的抽象方法的簽名(這裏的簽名指的是抽象方法的參數與返回類型)
行爲參數化:以前我們寫的方法都是將某個值或者Object作爲參數在方法內部進行相對應得操作,行爲參數化則是將操作值或者Object的行爲作爲參數傳遞

示例:
    從一個文件中讀取內容,通過行爲參數化來進行不同的操作

1.定義一個readFile的Operation接口,這個接口就是一個函數式接口,它符合函數式接口的定義,有且只有一個抽象接口
@FunctionalInterface
public interface Operation {
    public String readFile(BufferedReader bufferedReader) throws IOException;
}

2.在readOperation中使用operation接口的readFile方法,這一個方法的行爲會在第3步給出來
public class FileOperation {

    public String readOperation(Operation operation) throws IOException {
        //此處是java 7中新加的語法糖,帶資源的try語句不再需要我們顯式的去關閉資源
        try(BufferedReader bufferedReader = new BufferedReader(new FileReader("data.txt"))){
            return operation.readFile(bufferedReader);
        }
    }

}
3.將不同的行爲參數化,傳遞不同的Lambda表達式就可以有不同的方式處理文件內容
  String line1 = readOperation((BufferedReader bufferedReader) -> bufferedReader.readLine());
  String line2 = readOperation((BufferedReader bufferedReader) -> bufferedReader.readLine()+bufferedReader.readLine());
註明:

    a.@FunctionalInterface
在java8中,新的java API通過@FunctionalInterface來標註函數式接口,這個標註用於表示該接口會設計成一個函數式接口,標註的接口有多個抽象接口(沒有默認實現),會編譯異常“Multiple non-overriding abstract methods found in interface xxxInterface”,@FunctionalInterface不是必需的

    b.任何函數式接口都不允許拋出受檢異常(checked exception)。如果你需要Lambda表達式來拋出異常,有兩種辦法:定義一個自己的函數式接口,並聲明受檢異常,或者把Lambda包在一個try/catch塊中。

5.Lambda表達式中的類型檢查、類型推斷

Lambda表達式中的類型檢查

    Lambda的類型是從使用Lambda的上下文推斷出來的。上下文(比如,接受它傳遞的方法的參數,或接受它的值的局部變量)中Lambda表達式需要的類型稱爲目標類型。

檢查過程:

1.找出方法的聲明
    比如:public String readOperation(Operation operation) throws IOException
2.找到目標類型,比如Predicate<T -> Object>
    T -> Object:T綁定到某一個對象,如:String、自定義對象 等
3.找到函數式接口中定義的抽象方法
4.抽象方法描述了一個函數描述符,它接收的參數以及返回的類型
5.方法的任何實際參數都必須匹配這個要求

標註:

    只要不同抽象方法的參數與返回類型兼容,同一個Lambda表達式就可以與不同的函數式接口聯繫起來

Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42;
類型判斷:

    Java編譯器可以從上下文(目標類型)推斷出用什麼函數式接口來配合Lambda表達式
進一步簡化了Lambda表達式的寫法

--沒有類型推斷,參數bufferedReader有顯示參數
String line1 = readOperation((BufferedReader bufferedReader) -> bufferedReader.readLine());

--有類型推斷,參數bufferedReader沒有有顯示參數
String line3= readOperation(bufferedReader -> bufferedReader.readLine());

--只有一個參數的時候,參數兩邊的括號也能省略
Operation operation = bufferedReader -> bufferedReader.readLine();

6.Lambda和局部變量

  • Lambda允許使用方法主體外的變量,如同匿名內部類一樣,這樣的Lambda表達式稱爲捕獲Lambda-
  • Lambda使用這些變量是有限制的,只能是final修飾的變量
爲什麼會有這個限制

    1.存儲的位置不同,實例變量存儲在堆上,局部變量存儲在棧上
    2.Lambda 表達在另一個線程中執行,也就是可能存在局部變量在被回收掉的情況下,去訪問該局部變量;在用final修飾的情況下,去訪問的只是這個變量的副本

--正常
    int a = 10;
    Runnable runnable = () -> System.out.println(a);
    
--編譯報錯
--Error:從lambda 表達式引用的本地變量必須是最終變量或實際上的最終變量
    int a = 10;
    Runnable runnable = () -> System.out.println(a);
    
    a = 19;

總結:

  • Lambda沒有return語句,return語句是隱藏的 Lambda表達式可以包含多行語句 Lambda參數可以存在多個,也可以爲0個
  • Lambda表達式允許直接內聯,爲函數式接口的抽象方法提供實現,並且將整個表達式作爲函數式接口的一個實例
  • Lambda表達式的簽名要與函數式接口中定義的抽象方法簽名要一致 Lambda表達式會自動去推斷類型
  • Lambda表達式可以使用除主體外的變量,且主體外的變量必須用final修飾
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章