本文首發於 blog.zhaochunqi.com 轉載請註明 blog.zhaochunqi.comjava
根據JSR 335, Java 終於在 Java 8 中引入了 Lambda 表達式。也稱之爲閉包或者匿名函數。python
所謂的 JSR (Java Specification Requests) 全稱叫作 Java 規範提案。簡單來講就是向 Java 社區提交新的 API 或 服務 請求的提案。這些提案將做爲 Java 社區進行 Java 語言開發的需求,引導着開發的方向。express
JSR 335 的提案內容摘要以下:編程
This JSR will extend the Java Programming Language Specification and the Java Virtual Machine Specification to support the following features:閉包
也就是以下幾點:jvm
在 Java 8 中,以上均已經實現,以上內容下文均有介紹。ide
Lambda 表達式,其實就是代碼塊。函數
在具體瞭解 lambda 以前,咱們先日後退一步,看看以前咱們是如何處理這些代碼塊的!this
當決定在單獨的線程運行某程序時,你這樣作的spa
class Worker implements Runnable { public void run() { for (int i = 0; i < 1000; i++) doWork(); } ... }
這樣執行:
Worker w = new Worker(); new Thread(w).start();
Worker 中包含了你要執行的代碼塊。
若是你想實現根據字符串長度大小來排序,而不是默認的字母順序,你能夠本身來實現一個 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());
另一個例子,我選的是 Android 中的點擊事件,一樣是 Java:
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, "Hello World!", Toast.LENGTH_SHORT).show(); } });
它們都太複雜了啊!
上述例子都是在某個類中實現某個接口,而後傳遞到另一個方法中做爲參數,而後用來執行。
可是本質上,他們要傳遞的就是接口中那一個方法的實現而已啊!有必要先建立類,再實例化,再傳遞給調用的位置嗎?
由於 Java 是純面向對象的語言,像其餘語言那樣隨隨便便傳個方法過來,那可不行,必需要這樣。
在其餘語言中你可能能夠,可是,在Java 中,不能夠。
Java 設計人員爲了 Java 的簡潔跟連貫性,一直拒絕爲Java添加這種功能。(這也是我喜歡Java而不喜歡Python的緣由啊!!!)
通過多年的努力,開發人員終於找到了符合 Java 編程習慣的 Lambda 表達式!
考慮下前面的例子:
Integer.compare(first.length(), second.length())
first和second都是 String 類型,Java 是強類型的語言,必須指定類型:
(String first, String second) -> Integer.compare(first.length(), second.length())
看到沒有!第一個 Lambda 表達式誕生了!!輸入、輸出簡潔明瞭!
爲何叫 Lambda 呢,這個不少年之前,有位邏輯學家想要標準化的表示一些能夠被計算的數學方程(實際上存在,可是很難被表示出來),他就用 ℷ 來表示。
從新介紹一下 Java 中 Lambda 表達式的格式:
(參數) -> 表達式
若是計算的結果並不禁一個單一的表達式返回(換言之,返回值存在多種狀況),使用「{}",而後明確指定返回值。
(String first, String second) -> {
if (first.length() < second.length()) return -1; else if (first.length() > second.length()) return 1; else return 0; }
若是沒有參數,則 "()"中就空着。
() -> { 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 類型,由於 是一個 String 類型的 Comparator。
若是單個參數能夠被推斷出,你連括號均可以省略:
EventHandler<ActionEvent> listener = event ->
System.out.println("Thanks for clicking!"); // Instead of (event) -> or (ActionEvent event) ->
你能夠像對待其餘方法同樣,annotation,或者 使用 final 修飾符
(final String name) -> ... (@NonNull String name) -> ...
永遠不要定義 result 的類型,lambda 表達式老是從上下文中推斷出來的:
(String first, String second) -> Integer.compare(first.length(), second.length())
注意,在lambda 表達式中,某些分支存在返回值,某些不存在返回值這樣的狀況是不容許的。
如 (int x) -> { if (x >= 0) return 1; }
這樣是非法的。
要介紹 Java 中 lambda 表達式的實現,須要知道什麼是 函數式接口。
什麼叫做函數式接口呢(SAM)?
函數式接口指的是隻定義了惟一的抽象方法的接口(除了隱含的Object對象的公共方法), 所以最開始也就作SAM類型的接口(Single Abstract Method)。
Lambda 表達式向前兼容這些接口。
舉個例子 Array.sort:
Arrays.sort(words, (first, second) -> Integer.compare(first.length(), second.length()));
Array.sort() 方法收到一個實現了 Comparable<String> 接口的實例。
其實能夠把 Lambda 表達式想象成一個方法,而非一個對象,一個能夠傳入一個接口的方法。
再舉個例子
button.setOnClickListener(event ->
System.out.println("Thanks for clicking!"));
你看,是否是更易讀了呢?
Lambda 表達式可以向前兼容這些 interfaces, 太棒了! 那 Lambda 表達式還能幹什麼呢?
實際上,將函數式接口轉變成 lambda 表達式是你在 Java 中惟一能作的事情。
Why ?!!
在其餘的語言中,你能夠定義一些方便的方法類型,但在 Java 中,你甚至不能將一個Lambda表達式賦值給類型爲 Object 的變量,由於 Object 變量不是一個 Functional Interface。
Java 的設計者們堅持使用熟悉的 interface 概念而不是爲其引入新的 方法類型。
(這裏我還要爲設計者點贊!謹慎的設計,一方面下降了初學者的門檻,一方面方便了高級用戶的使用。對比 python2和 python3,升級的不兼容讓不少人一直停留在 python2)
能不能再簡潔一點?有的時候咱們所要作的事情不過是調用其餘類中方法來處理事件。
button.setOnClickListener(event -> System.out.println(event));
若是這樣呢?
button.setOnAction(System.out::println);
表達式 System.out::println
屬於一個方法引用(method reference), 至關於 lambda 表達式 x -> System.out.println(x)
再舉個例子,若是你想對字符串無論大小寫進行排序,就能夠這樣寫!
Arrays.sort(strings, String::compareToIgnoreCase)
如上所見 ::
操做符將方法名與實例或者類分隔開。整體來講,又以下的規則:
值得指出的是, this
和super
關鍵字能夠在其中使用:
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(); } }
跟上一個差很少,畢竟構造方法 也是方法啊!!不過方法名字爲 new 。
可是!這個構造方法引用有一個牛逼的地方!
你知道 Array 是不能使用範型的對吧!(什麼,你不知道?看看這裏 http://stackoverflow.com/questions/2927391/whats-the-reason-i-cant-create-generic-array-types-in-java),你沒有辦法建立一個類型爲 T 的 Array 。 new T[n] 將會被覆蓋爲 new Object[n]。
假設咱們想要一個包含 buttons 的 Array。Stream interface 能夠返回一個 Object array。
Object[] buttons = stream.toArray();
不不不,咱們可不想要 Object。Stream 庫使用 構造方法引用解決了這個問題:
Button[] buttons = stream.toArray(Button[]::new);
注意到咱們在題目中寫着 閉包(closure),實際上,閉包的定義是: 引用了自由變量的函數。
在以前,若是須要在匿名類的內部引用外部變量,須要將外部變量定義爲 final ,如今有了 lambda 表達式,你沒必要再這麼作了。但一樣須要保證外部的自由變量不能在 lambda 表達式中被改變。
這是什麼意思呢? 不須要定義爲 final,也不能改?
其實理解起來很簡單,Java 8 中,不須要定義爲 final ,但你其實能夠直接把他看成 final,不要試圖修改它就好了。
即使你用內部類,如今也無需定義爲 final 了。
參考 StackOverFlow 連接: http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class
因爲歷史緣由,像是相似 Collection 這種接口,若是進行添加接口的話,那將會形成以前的代碼出錯。
Java 想了一個一勞永逸的方法解決這個問題, 使用 default 修飾符來提供默認的實現
好比 Collection 接口的源代碼:
default void remove() { throw new UnsupportedOperationException("remove"); }
當沒有 override remove 這個方法是,調用的時候返回 UnsupportedOperationException 錯誤。
Java 8 中,你能夠在接口中添加靜態方法了。 可能與你想的不太同樣,可是呢,爲了方便起見,如今 interface 能夠有靜態方法了。
參考連接: