Java Lambda 表达式(又名闭包 (Closure)/ 匿名函数 ) 笔记
Java Lambda 表達(dá)式(又名閉包 (Closure)/ 匿名函數(shù) ) 筆記
根據(jù) JSR 335, Java 終于在 Java 8 中引入了 Lambda 表達(dá)式。也稱之為閉包或者匿名函數(shù)。
JSR 335
所謂的 JSR (Java Specification Requests) 全稱叫做 Java 規(guī)范提案。簡單來說就是向 Java 社區(qū)提交新的 API 或 服務(wù) 請求的提案。這些提案將作為 Java 社區(qū)進(jìn)行 Java 語言開發(fā)的需求,引導(dǎo)著開發(fā)的方向。
JSR 335 的提案內(nèi)容摘要如下:
This JSR will extend the Java Programming Language Specification and the Java Virtual Machine Specification to support the following features:
- ambda Expressions
- SAM Conversion
- Method References
- Virtual Extension Methods
也就是如下幾點(diǎn):
為什么需要 Lambda 表達(dá)式?
Lambda 表達(dá)式,其實(shí)就是代碼塊。
原來怎么處理
在具體了解 lambda 之前,我們先往后退一步,看看之前我們是如何處理這些代碼塊的!
例子一
當(dāng)決定在單獨(dú)的線程運(yùn)行某程序時(shí),你這樣做的
class Worker implements Runnable {public void run() {for (int i = 0; i < 1000; i++)doWork();}...}這樣執(zhí)行:
Worker w = new Worker(); new Thread(w).start();Worker 中包含了你要執(zhí)行的代碼塊。
例子二
如果你想實(shí)現(xiàn)根據(jù)字符串長度大小來排序,而不是默認(rèn)的字母順序,你可以自己來實(shí)現(xiàn)一個(gè) Comparator 用來 Sort
class LengthComparator implements Comparator<String> {public int compare(String first, String second) {return Integer.compare(first.length(), second.length());}}Arrays.sort(strings, new LengthComparator());####例子三
另外一個(gè)例子,我選的是 Android 中的點(diǎn)擊事件,同樣是 Java:
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Toast.makeText(MainActivity.this, "Hello World!", Toast.LENGTH_SHORT).show();} });###上面代碼有什么問題呢?###
它們都太復(fù)雜了啊!
上述例子都是在某個(gè)類中實(shí)現(xiàn)某個(gè)接口,然后傳遞到另外一個(gè)方法中作為參數(shù),然后用來執(zhí)行。
但是本質(zhì)上,他們要傳遞的就是接口中那一個(gè)方法的實(shí)現(xiàn)而已啊!有必要先創(chuàng)建類,再實(shí)例化,再傳遞給調(diào)用的位置嗎?
因?yàn)?Java 是純面向?qū)ο蟮恼Z言,像其他語言那樣隨隨便便傳個(gè)方法過來,那可不行,必須要這樣。
在其他語言中你可能可以,但是,在Java 中,不可以。
Java 設(shè)計(jì)人員為了 Java 的簡潔跟連貫性,一直拒絕為 Java 添加這種功能。(這也是我喜歡 Java 而不喜歡 Python 的原因啊!!!)
經(jīng)過多年的努力,開發(fā)人員終于找到了符合 Java 編程習(xí)慣的 Lambda 表達(dá)式!
Lambda 表達(dá)式語法(Syntax)
考慮下前面的例子:
Integer.compare(first.length(), second.length())first 和 second 都是 String 類型,Java 是強(qiáng)類型的語言,必須指定類型:
(String first, String second)-> Integer.compare(first.length(), second.length())
看到?jīng)]有!第一個(gè) Lambda 表達(dá)式誕生了!!輸入、輸出簡潔明了!
為什么叫 Lambda 呢,這個(gè)很多年以前,有位邏輯學(xué)家想要標(biāo)準(zhǔn)化的表示一些可以被計(jì)算的數(shù)學(xué)方程(實(shí)際上存在,但是很難被表示出來),他就用 ? 來表示。
重新介紹一下 Java 中 Lambda 表達(dá)式的格式:
(參數(shù)) -> 表達(dá)式
多返回值
(String first, String second) -> {if (first.length() < second.length()) return -1;else if (first.length() > second.length()) return 1;else return 0;}
如果計(jì)算的結(jié)果并不由一個(gè)單一的表達(dá)式返回(換言之,返回值存在多種情況),使用“{}”,然后明確指定返回值。
(String first, String second) -> {if (first.length() < second.length()) return -1;else if (first.length() > second.length()) return 1;else return 0; }###無參數(shù)
如果沒有參數(shù),則 “()”中就空著。
() -> { for (int i = 0; i < 1000; i++) doWork(); }###省略###
Comparator<String> comp= (first, second) // Same as (String first, String second)-> Integer.compare(first.length(), second.length());這里,first 和 second 可以被推斷出是 String 類型,因?yàn)?是一個(gè) String 類型的 Comparator。
如果單個(gè)參數(shù)可以被推斷出,你連括號都可以省略:
EventHandler<ActionEvent> listener = event ->System.out.println("Thanks for clicking!");// Instead of (event) -> or (ActionEvent event) ->###修飾符
你可以像對待其他方法一樣,annotation,或者 使用 final 修飾符
(final String name) -> ...(@NonNull String name) -> ...永遠(yuǎn)不要 定義 result 的類型,lambda 表達(dá)式總是從上下文中推斷出來的:
(String first, String second) -> Integer.compare(first.length(), second.length())###注意
注意,在 lambda 表達(dá)式中,某些分支存在返回值,某些不存在返回值這樣的情況是不允許的。
如 (int x) -> {if (x >= 0) return 1; }這樣是非法的。
函數(shù)式接口 (Functional Interfaces/SAM)
要介紹 Java 中 lambda 表達(dá)式的實(shí)現(xiàn),需要知道什么是 函數(shù)式接口。
什么叫作函數(shù)式接口呢 (SAM)?
函數(shù)式接口指的是只定義了唯一的抽象方法的接口(除了隱含的 Object 對象的公共方法), 因此最開始也就做 SAM 類型的接口(Single Abstract Method)。
Lambda 表達(dá)式 向前兼容 這些接口。
Comparable
舉個(gè)例子 Array.sort:
Arrays.sort(words,(first, second) -> Integer.compare(first.length(), second.length()));Array.sort() 方法收到一個(gè)實(shí)現(xiàn)了 Comparable接口的實(shí)例。
其實(shí)可以把 Lambda 表達(dá)式想象成一個(gè)方法,而非一個(gè)對象,一個(gè)可以傳入一個(gè)接口的方法。
OnClickListener
再舉個(gè)例子
button.setOnClickListener(event ->System.out.println("Thanks for clicking!"));你看,是不是更易讀了呢?
Lambda 表達(dá)式能夠向前兼容這些 interfaces, 太棒了! 那 Lambda 表達(dá)式還能干什么呢?
實(shí)際上,將函數(shù)式接口轉(zhuǎn)變成 lambda 表達(dá)式是你在 Java 中 唯一 能做的事情。
Why ?!!
在其他的語言中,你可以定義一些方便的方法類型,但在 Java 中,你甚至不能將一個(gè) Lambda 表達(dá)式賦值給類型為 Object 的變量,因?yàn)?Object 變量不是一個(gè) Functional Interface。
Java 的設(shè)計(jì)者們堅(jiān)持使用熟悉的 interface 概念而不是為其引入新的 方法類型。
(這里我還要為設(shè)計(jì)者點(diǎn)贊!謹(jǐn)慎的設(shè)計(jì),一方面降低了初學(xué)者的門檻,一方面方便了高級用戶的使用。對比 python2 和 python3,升級的不兼容讓很多人一直停留在 python2)
Method References
能不能再簡潔一點(diǎn)?有的時(shí)候我們所要做的事情不過是調(diào)用其他類中方法來處理事件。
button.setOnClickListener(event -> System.out.println(event));如果這樣呢?
button.setOnAction(System.out::println);表達(dá)式 System.out::println 屬于一個(gè)方法引用(method reference), 相當(dāng)于 lambda 表達(dá)式 x -> System.out.println(x)
再舉個(gè)例子,如果你想對字符串不管大小寫進(jìn)行排序, 就可以這樣寫!
Arrays.sort(strings, String::compareToIgnoreCase)如上所見 ::操作符將方法名與實(shí)例或者類分隔開。總體來說,又如下的規(guī)則:
- object::instanceMethod
- Class::staticMethod
- Class::instanceMethod
值得指出的是, this和 super 關(guān)鍵字可以在其中使用:
class Greeter {public void greet() {System.out.println("Hello, world!");}}.
class ConcurrentGreeter extends Greeter {public void greet() {Thread t = new Thread(super::greet);t.start();} }構(gòu)造方法引用 Constructor References
跟上一個(gè)差不多,畢竟 構(gòu)造方法 也是方法啊!!不過方法名字為 new 。
但是!這個(gè)構(gòu)造方法引用有一個(gè)牛逼的地方!
你知道 Array 是不能使用范型的對吧!(什么,你不知道?看看這里 http://stackoverflow.com/ques... 你沒有辦法創(chuàng)建一個(gè)類型為 T 的 Array 。 new T[n] 將會(huì)被覆蓋為 new Object[n]。
假設(shè)我們想要一個(gè)包含 buttons 的 Array。Stream interface 可以返回一個(gè) Object array。
Object[] buttons = stream.toArray();不不不,我們可不想要 Object。Stream 庫使用 構(gòu)造方法引用解決了這個(gè)問題:
Button[] buttons = stream.toArray(Button[]::new);
變量作用域
注意到我們在題目中寫著 閉包(closure), 實(shí)際上,閉包的定義是: 引用了自由變量的函數(shù)。
在之前,如果需要在匿名類的內(nèi)部引用外部變量,需要將外部變量定義為 final ,現(xiàn)在有了 lambda 表達(dá)式,你不必再這么做了。但同樣需要保證外部的自由變量不能在 lambda 表達(dá)式中被改變。
這是什么意思呢? 不需要定義為 final,也不能改?
其實(shí)理解起來很簡單,Java 8 中,不需要定義為 final ,但你其實(shí)可以直接把他當(dāng)作 final,不要試圖修改它就行了。
即便你用內(nèi)部類,現(xiàn)在也無需定義為 final 了。
參考 StackOverFlow 鏈接: http://stackoverflow.com/ques...
Default Methods
由于歷史原因,像是類似 Collection 這種接口,如果進(jìn)行添加接口的話,那將會(huì)造成之前的代碼出錯(cuò)。
Java 想了一個(gè)一勞永逸的方法解決這個(gè)問題, 使用 default 修飾符來提供默認(rèn)的實(shí)現(xiàn)
比如 Collection 接口的源代碼:
default void remove() {throw new UnsupportedOperationException("remove"); }當(dāng)沒有 override remove 這個(gè)方法是,調(diào)用的時(shí)候返回 UnsupportedOperationException 錯(cuò)誤。
Static Methods in Interfaces
Java 8 中,你可以在接口中添加靜態(tài)方法了。 看起來好像并不符合接口的定義了。
一般用來生成一個(gè)簡單實(shí)現(xiàn)該 interface 的實(shí)例。
參考鏈接:
JSR 335: Lambda Expressions for the JavaTM Programming Language
Java 8 新特性概述
Lambda Expressions in Java 8
總結(jié)
以上是生活随笔為你收集整理的Java Lambda 表达式(又名闭包 (Closure)/ 匿名函数 ) 笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: su命令 sudo命令 限制root远程
- 下一篇: VMM系列之VMM角色介绍以及创建运行方