Java8 lambda表達式語法

2021-01-18 計算機java編程

文中如有錯誤和理解偏差的地方,希望大家幫忙指出,我會持續修改和優化。本文是該系列的第一篇,主要介紹Java8對屌絲碼農最有吸引力的一個特性—lambda表達式。

java8的安裝

工欲善其器必先利其器,首先安裝JDK8。過程省略,大家應該都可以自己搞定。但是有一點這裡強調一下(Windows系統):目前我們工作的版本一般是java 6或者java 7,所以很多人安裝java8基本都是學習為主。這樣就在自己的機器上會存在多版本的JDK。而且大家一般是希望在命令行中執行java命令是基於老版本的jdk。但是在安裝完jdk8並且沒有設置path的情況下,你如果在命令行中輸入:java -version,屏幕上會顯示是jdk 8。這是因為jdk8安裝的時候,會默認在C:/Windows/System32中增加java.exe,這個調用的優先級比path設置要高。所以即使path裡指定是老版本的jdk,但是執行java命令顯示的依然是新版本的jdk。這裡我們要做的就是刪除C:/Windows/System32中的java.exe文件(不要手抖!)。

Lambda初體驗

下面進入本文的正題–lambda表達式。首先我們看一下什麼是lambda表達式。以下是維基百科上對於」Lambda expression」的解釋:

a function (or a subroutine) defined, and possibly called, without being bound to an identifier。

簡單點說就是:一個不用被綁定到一個標識符上,並且可能被調用的函數。這個解釋還不夠通俗,lambda表達式可以這樣定義(不精確,自己的理解):一段帶有輸入參數的可執行語句塊。這樣就比較好理解了吧?一例勝千言。有讀者反饋:不理解Stream的含義,所以這裡先提供一個沒用stream的lambda表達式的例子。

上面兩段代碼分別是:使用lambda表達式來排序和使用匿名內部類來排序。這個例子可以很明顯的看出lambda表達式簡化代碼的效果。接下來展示lambda表達式和其好基友Stream的配合。

這段代碼就是對一個字符串的列表,把其中包含的每個字符串都轉換成全小寫的字符串(熟悉Groovy和Scala的同學肯定會感覺很親切)。注意代碼第四行的map方法調用,這裡map方法就是接受了一個lambda表達式(其實是一個java.util.function.Function的實例,後面會介紹)。

為什麼需要Lambda表達式呢?在嘗試回答這個問題之前,我們先看看在Java8之前,如果我們想做上面代碼的操作應該怎麼辦。

先看看普通青年的代碼:

接下來看看文藝青年的代碼(藉助Guava):

在此,我們不再討論普通青年和文藝青年的代碼風格孰優孰劣(有興趣的可以去google搜索「命令式編程vs聲明式編程」)。本人更加喜歡聲明式的編程風格,所以偏好文藝青年的寫法。但是在文藝青年代碼初看起來看起來幹擾信息有點多,Function匿名類的構造語法稍稍有點冗長。所以Java8的lambda表達式給我們提供了創建SAM(Single Abstract Method)接口更加簡單的語法糖。

Lambda語法詳解

我們在此抽象一下lambda表達式的一般語法:

從lambda表達式的一般語法可以看出來,還是挺符合上面給出的非精確版本的定義–「一段帶有輸入參數的可執行語句塊」。

上面的lambda表達式語法可以認為是最全的版本,寫起來還是稍稍有些繁瑣。別著急,下面陸續介紹一下lambda表達式的各種簡化版:

1.參數類型省略–絕大多數情況,編譯器都可以從上下文環境中推斷出lambda表達式的參數類型。這樣lambda表達式就變成了:

所以我們最開始的例子就變成了(省略了List的創建):

2.當lambda表達式的參數個數只有一個,可以省略小括號。lambda表達式簡寫為:

所以最開始的例子再次簡化為:

3.當lambda表達式只包含一條語句時,可以省略大括號、return和語句結尾的分號。lambda表達式簡化為:

param1 -> statment

所以最開始的例子再次簡化為:

4.使用Method Reference(具體語法後面介紹)

Lambda表達式眼中的外部世界

我們前面所有的介紹,感覺上lambda表達式像一個閉關鎖國的傢伙,可以訪問給它傳遞的參數,也能自己內部定義變量。但是卻從來沒看到其訪問它外部的變量。是不是lambda表達式不能訪問其外部變量?我們可以這樣想:lambda表達式其實是快速創建SAM接口的語法糖,原先的SAM接口都可以訪問接口外部變量,lambda表達式肯定也是可以(不但可以,在java8中還做了一個小小的升級,後面會介紹)。

上面的這個例子中,map中的lambda表達式訪問外部變量Integer i。並且可以訪問外部變量是lambda表達式的一個重要特性,這樣我們可以看出來lambda表達式的三個重要組成部分:

輸入參數可執行語句存放外部變量的空間不過lambda表達式訪問外部變量有一個非常重要的限制:變量不可變(只是引用不可變,而不是真正的不可變)。

上面的代碼,會報編譯錯誤。因為變量i被lambda表達式引用,所以編譯器會隱式的把其當成final來處理(ps:大家可以想像問什麼上一個例子不報錯,而這個報錯。)細心的讀者肯定會發現不對啊,以前java的匿名內部類在訪問外部變量的時候,外部變量必須用final修飾。Bingo,在java8對這個限制做了優化(前面說的小小優化),可以不用顯示使用final修飾,但是編譯器隱式當成final來處理。

lambda眼中的this

在lambda中,this不是指向lambda表達式產生的那個SAM對象,而是聲明它的外部對象。

方法引用(Method reference)和構造器引用(construct reference)

方法引用

前面介紹lambda表達式簡化的時候,已經看過方法引用的身影了。方法引用可以在某些條件成立的情況下,更加簡化lambda表達式的聲明。方法引用語法格式有以下三種:

前兩種方式類似,等同於把lambda表達式的參數直接當成instanceMethod|staticMethod的參數來調用。比如System.out::println等同於x->System.out.println(x);Math::max等同於(x, y)->Math.max(x,y)。

最後一種方式,等同於把lambda表達式的第一個參數當成instanceMethod的目標對象,其他剩餘參數當成該方法的參數。比如String::toLowerCase等同於x->x.toLowerCase()。

構造器引用

構造器引用語法如下:ClassName::new,把lambda表達式的參數當成ClassName構造器的參數 。例如BigDecimal::new等同於x->new BigDecimal(x)。

吐槽一下方法引用

表面上看起來方法引用和構造器引用進一步簡化了lambda表達式的書寫,但是個人覺得這方面沒有Scala的下劃線語法更加通用。比較才能看出,翠花,上代碼!

上面的這段代碼就是給定一個String類型的List,獲取每個String的首字母,並將其組合成新的List。這段代碼就沒辦法使用方法引用來簡化。接下來,我們簡單對比一下Scala的下劃線語法(不必太糾結Scala的語法,這裡只是做個對比):

在Scala中基本不用寫lambda表達式的參數聲明。

相關焦點

  • Java中Lambda表達式的5種不同語法
    1.標準語法考慮以下示例:String[] arr = {"program", "creek", "is", "a", "java", "site"};Arrays.sort(arr, (String m, String n) -> Integer.compare(m.length(), n.length()));System.out.println(Arrays.toString(arr));lambda表達式的標準語法包括以下內容:
  • Java8 lambda表達式
    ②展示了只有一個參數的lambda的表達式。不同於只包含一條表達式的lambda。如果多次為一個變量賦值,並且單算在lambda表達式中進行引用,那麼會得到一個變異錯誤。這也是為什麼人們把lambda表達式當做閉包看待,在程式語言的爭論中,有許多關於java是否有閉包的爭論,因為其只能引用final變量,為了避免這一個無端的爭論,我在本書中將他們稱之為lambda表達式。
  • 大家都在說的「Lambda 表達式」到底是什麼?
    自從java8中引入了Lambda表達式之後,使用的人也越來越多了,這篇文章我們就來看看什麼是Lambda表達式。廢話少說,先看定義!2、簡單了解一下lambda的語法格式(參數) ->{語句; }上面就是lambda的語法格式,「->」是它的一個標誌,當你看到「->」你就知道這裡使用了Lambda表達式。3、再來看一下Lambda的特徵可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。
  • Python中的Lambda表達式
    Lambda表達式當我們需要做一些簡單的事情並且更希望快速完成工作而不是正式命名函數時,Lambda表達式是理想的選擇。Lambda表達式也稱為匿名函數。Python中的Lambda表達式是聲明小型匿名函數的一種簡短方式(沒有必要為Lambda函數提供名稱)。
  • Lambda表達式在Python事件中的運用
    本篇筆記內容:Lambda表達式詳解Lambda表達式在事件中的應用【1】Lambda表達式詳解lambda 表達式定義的是一個匿名函數,只適合簡單輸入參數,簡單計算返回結果,不適合功能複雜情況。lambda 定義的匿名函數也有輸入、也有輸出,只是沒有名字。語法格式如下:lambda 參數值列表:表達式其中,參數值列表即為輸入,表達式計算的結構即為輸出。看一個案例:求三個數的和。
  • Python每天一分鐘:lambda表達式 (匿名函數)及用法詳解
    lambda表達式介紹python中有一種靈活,便捷的且具有函數功能的表達式:lambda表達式!lambda表達式,又稱匿名函數,是現代各種程式語言爭相引入的一種語法,其功能堪比函數,設計卻比函數簡潔。
  • Python學習第40課-Lambda表達式創建匿名函數
    Python中可以使用Lambda表達式來創建匿名函數。lambda表達式用來創建匿名函數,那麼我們也可以把lambda表達式叫做lambda函數。●Lambda函數的特點lambda函數只需要寫一行代碼,函數體比用def定義的函數體簡單。lambda函數有自己的命名空間,且不能訪問自己命名空間之外或全局命名空間裡的參數。
  • 「數據清洗」lambda表達式配合使用的四種函數
    標籤:數據清洗、pythonlambda表達式配合使用的四種函數一、什麼是lambda表達式基本特性使用方法filter函數map函數sorted函數reduce函數總結什麼是lambda表達式>lambda 表達式是一個匿名函數,lambda表達式基於數學中的λ演算得名,直接對應於其中的lambda抽象,是一個匿名函數,即沒有函數名的函數。
  • 三種基本用法、五種應用場景,理清C++11新特性:Lambda表達式
    lambda表達式是指能夠捕獲作用域中的變量的無名函數對象,狹義的理解,就是匿名函數。無論是在項目中,還是在開源網站,總是能夠看到lambda的身影。為了能夠輕鬆閱讀代碼,進而熟練地使用,本文首先將講解lambda表達式的基本語法、三種基本用法,然後介紹五種實際的應用場景,最有總結說明lambda表達式的作用。
  • Python匿名函數:Lambda表達式
    我們以一張圖形進入主題:從圖中我們可以看出lambda表達式幾點特徵:簡潔性,符合了Python的一貫宗旨;起到了函數的作用,但未顯示函數名稱,這就是匿名函數;接著我們嘗試用lambda表達式來實現上述函數功能:# labmda表達式psum = lambda a,b,c:a + b + cprint(psum) # 驗證是否生成一個新對象
  • 給JAVA程式設計師的正則表達式一課
    正則表達式可以用於:驗證用戶的輸入。搜索給定數據中的文本。(可在文本編輯器中使用)編譯器中的解析器語法突顯,數據包嗅探器等。要全面了解Regex,我們要理解基本知識,下面我們分別介紹,示例中我們用到在線正則解析網站regex101。基本量詞(*+?
  • 詳解Java表達式與運算符
    在java語言中,定義常量的語法如下:final 數據類型 常量名稱 = 值;其中,final是Java關鍵字,數據類型可以是Java語言支持的任何數據類型。表達式的計算結果一般為數值,如果表達式是一個關係表達式或邏輯表達式,表達式會返回一個布爾值,即真或假。根據運算符的不同,可以把表達式分為賦值表達式、算術表達式、關係表達式和邏輯表達式。賦值表達式用於把等號右邊的操作數賦值給等號左邊的變量;算術表達式用於數值運算;關係表達式用於條件判斷;邏輯表達式用於邏輯操作。
  • python入門基礎之lambda匿名函數詳解
    一個完整的 lambda"語句"代表了一個表達式,這個表達式的定義體必須和聲明放在同一行。語法如下:lambda [arg1[, arg2, ... argN]]: expression 參數是可選的,如果使用的參數話,參數通常也是表達式的一部分2、lambda匿名函數與def區別lambda 和def它兩個的基本用法差不多
  • CSharp 基礎知識系列- 6 Lambda表達式和Linq簡單介紹
    嗯,簡單來講就是匿名函數,我們不聲明方法名,只寫一個方法體,這個方法體就是lambda表達式1. lambda表達式1.1 如何寫一個lambda表達式首先,在寫lambda表達式之前,需要先了解兩個特殊的類型:Func和Action。這是兩個委託,這裡先不急著了解什麼是委託,可以把它們當做一種名稱規範就行,它們都可以表示一個方法。
  • lambda匿名函數都自嘆不如
    lambda表達式在學習Python的過程中,很多人常常對lambda表達式的語法感到困惑,什麼是lambda?lambda表達式怎麼用,什麼時候該用lambda?請看下去,下面內容將為你介紹。Python中的lambda是用來創建匿名函數,它是一個表達式,但函數體比def簡單,它的語法如下:語法參數中,arg為入口參數,expression為函數體,用函數表達則如下圖所示:用學習函數的方式來學習lambda表達式就非常容易理解了,在這裡lambda簡化了函數定義的書寫形式,使得代碼更簡潔。
  • Java Lambda表達式的3種簡寫方式
    使用 Lambda 表達式是為了簡化程序代碼,Lambda 表達式本身也提供了多種簡化形式,這些簡化形式雖然簡化了代碼,但客觀上使得代碼可讀性變差
  • JAVA歷史版本
    java發展時間線 JAVA發展 1.1996年1月23日 JDK 1.0 5.2002年2月13日 JDK 1.4 很多公司參與 正則,異常鏈,N|O,日誌類,XML解析器,XSLT轉換器 6.2004年9月30日 JDK 5 (放棄之前1.x命名方式) 語法大改進
  • Python面試題推薦:什麼是lambda函數?
    定義lambda函數的形式如下:labmda 參數:表達式lambda函數默認返回表達式的值。你也可以將其賦值給一個變量。lambda函數可以接受任意個參數,包括可選參數,但是表達式只有一個:>>> g = lambda x, y: x*y>>> g(3,4)12>>> g = lambda x, y=0,
  • 挑戰Java 霸主之位?C# 五個不可替代的特性瞬間秒殺 Java
    (List numbers)return numbers.Where(a => a > 9 && a < 100).ToList();這裡兩種語法都是正確的,唯一的區別就是查詢語法看起來更像是 SQL 語句而方法語法使用 lambda 表達式(當然,看起來很像我們在 Java 裡寫的某些代碼)綜述:LINQ 所依賴的許多特性