Java8-Lambda表示式

sunankang發表於2020-12-29

Lambda表示式,它可以很簡潔地表示一個行為或傳遞程式碼,現在你可以把Lambda表示式看作匿名功能,它基本上就是沒有宣告名稱的方法,但和匿名類一樣,它也可以作為引數傳遞給一個方法

歡迎訪問本人部落格:http://wangnan.tech

Lambda管中窺豹

可以把lambda表示式理解為簡潔地表示可傳遞的匿名函式的一種方式:它沒有名稱,但它有引數列表,函式主體,返回型別,可能還有一個可以丟擲的異常列表

lambda表示式有三部分:

  • 引數列表
  • 箭頭
  • lambda主體
79431-dfdeeb7b08526c3a.png

有效的lambda表示式舉例:

(String s)->s.length()

這個lambda表示式具有一個string型別的引數,返回一個int,lambda沒有return語句,因為已經隱含了return

(Apple a)->a.getWeigh()>150

這個lambda表示式有一個Apple型別的引數並返回一個boolean

(int x,int y)->{
    System.out.println("Result:");
    System.out.println(x+y);   
}

這個lambda表示式具有兩個int型別的引數,沒有返回值,注意lambda表示式可以包含多行語句

()->42

這個lambda表示式沒有引數,返回一個int

lambda表示式的基本語法是:
(parameters)-> expression
或:
(parameters)-> {expression}

下面哪個不是有效的lambda表示式?

()->{}

有效,lambda沒有引數,返回void

()->"Raoul"

有效,lambda沒有引數,返回string

()->{return "Mario";}

有效,lambda沒有引數,返回string,可以顯示的寫返回語句

(Integer i)->return "Alan"+i;

無效,需要使用花括號,如下:(Integer i)->{return "Alan"+i;}

(String s)->("IronMan";)

"IronMan"是一個表示式,不是一個語句,要使用此lambda有效,你可以去除花括號
或者,可以顯示的返回如下:(String s)->{return "IronMan"}

在哪裡以及如何使用Lamdda

可以在函式式介面上使用lambda

函式式介面

函式式介面就是指定義一個抽象方法的介面
比如:

public interface Predicate<T>{
    boolean test (T t)
}
public interface Compartor<T>{
    int  compare(T o1,T o2);
}
public interface Runnable{
    void run();
}

我們還知道,介面現在還可以擁有預設方法,哪怕有很多預設方法,只要介面只定義一個抽象方法,它就仍然是一個函式式介面

@FunctionalInterface

這個標註用於表示該介面會設計成一個函式式介面,如果你用了這個註解,而它卻不是一個函式式介面的話,編譯器將返回一個提示原因的錯誤

使用函式式介面

為了應用不同的lambda表示式,你需要一套能夠描述常見函式描述符的函式式介面
比如之間我們見過的Comparable Runnable Callable

Predicate

java.util.function.Predicate<T> 介面定義了一個名叫test的抽象方法,它接受泛型T物件,並返回一個boolean,在你需要一個涉及型別T的布林表示式時,就可以使用這個介面

consumer

java.util.funtion.Consumer<T> 定義了一個名叫accept的抽象方法,它接受泛型T的物件,沒有返回,你如果需要訪問型別T物件,並對其進行某些操作,可以使用這個介面

Function

java.util.funtion.Funtion<T,R>介面定義了一個叫作apply的方法,它接受一個泛型T的物件,並返回一個泛型R的物件,如果你需要定義一個lambda,將輸入物件的資訊對映到輸出,就可以使用這個介面

java8中常用的函式式介面

79431-766536e8be336586.png
79431-a9fc8d2afa8c6cf8.png

lambda及函式式介面的例子

79431-c74d6e8a0730e00a.png

型別檢查,型別推斷以及限制

型別檢查

lambda的型別時從使用拉門等到的上下文推斷出來的,比如,接受它傳遞的方法的引數,或接受它的值的區域性變數

lambda表示式型別檢查過程

79431-032bf71fb326f02d.png

使用區域性變數

lambda表示式允許使用自由變數(不是引數,而是在外層作用域中定義的變數)
例如:

int portNumber = 1337;
Runnable r = {}->System.out.println(portNumber)

儘管如此,有一些限制,lambda可以沒有限制地捕獲例項變數和靜態變數,但是區域性變數必須顯示宣告為final或事實上是final。換句話說,lambda表示式只能捕獲指派給他們的區域性變數一次

為什麼?

  1. 例項變數和區域性變數背後的實現有一個關鍵不同,例項變數儲存在堆中,而區域性變數保證在棧上,如果lambda可以訪問區域性變數,而且lambda是在一個執行緒中使用的,則是喲lambda執行緒可能會分配該變數的執行緒將這個變數回收之後,去訪問該變數。因此,java在訪問自由區域性變數時,實際上在是在訪問它的副本,而不是訪問原始變數,
  2. 這一限制不鼓勵你使用改變區域性變數的典型指令式程式設計模式,這種模式會阻礙並行處理

方法引用

方法引用讓你可以重複使用現有的方法定義,並像Lambda一樣傳遞他們,他們似乎更易讀,感覺也更自然
例如
先前:

inventory.sort((Apple a1,Apple a2)
                ->a1.getWeigh().compareTo(a2.getWeight()));

之後(使用方法引用和java.util.Comparator.comparing)

iventory.sort(comparting(Apple::getWeight));

方法應用可以被看作僅僅呼叫特定方法的lambda的一種快捷寫法,當你需要使用方法引用時,目標引用放在分隔符::前。方法的名稱放在後面,不需要括號,因為沒有實際呼叫這個方法

複合lambda表示式

你可以把多個簡單的lambda複合成複雜的表示式,比如兩個謂詞進行一個or操作,還可以讓一個函式的結果成為另一個函式的輸入

比如

逆序

inventory.sort(comparing(Apple::getWeight).reversed())

比較器鏈

inventory.sort(comparing(Apple::getWeight)
            .reversed()
            .thenComparing(Apple:getCountry()));

謂詞複合

三個方法:negate() and() or()

函式複合

andThen() 意味著g(f(x))
compose() 意味著f(g(x))

小結

  • lambda表示式可以理解為一種匿名函式
  • 函式式介面就是僅僅宣告瞭一個抽象方法的介面
  • 只有在接受函式式介面的地方才可以使用Lambda表示式
  • java8自帶了一些常用的函式式介面,放在java.util.function包裡
  • 方法引用讓你重複使用現有的方法並直接傳遞他們

(注:內容整理自《Java8實戰》)

歡迎關注我的微信訂閱號


79431-f93494023e1a7ed3.jpg

歡迎關注我的開發者頭條獨家號搜尋:269166

相關文章