Java 8:Lambda表達式試水
Java 8 還要有一陣才能發(fā)布,而且它會帶來一個我非常期待的語言特性:Lambda表達式。
不幸的是,其他的Java平臺大的新特性和模塊都已經(jīng)被推遲到了Java 9。不管怎樣,引入lambda表達式(如果你喜歡的話,也可以稱它為閉包),會使得Java的編程體驗更棒。
還有一段時間的等待——不過Java的開發(fā)現(xiàn)在是開源的,我們現(xiàn)在可以看一看并且嘗試一下。我們開始吧!
下載和安裝啟用了Lambda的Java 8
最開始,我還以為我需要自己去編譯Java 8,因為它還沒有發(fā)布。不過,讓我驚奇的是,在http://jdk8.java.net/lambda/ 已經(jīng)提供了所有平臺可用的二進制版本。因此我直接下載了***的開發(fā)者預覽版本,然后安裝在我的電腦上。
為了確保它是正常運行的,我創(chuàng)建了一個LambadIntro類,它會輸出“Hello, World!”,編譯然后執(zhí)行它:
- ~ $ export JAVA_HOME=~/Devtools/Java/jdk1.8.0/
- ~ $ cd spikes/lambda-water
- ~ $ $JAVA_HOME/bin/javac src/net/jthoenes/blog/spike/lambda/LambdaIntro.java
- ~ $ $JAVA_HOME/bin/java -cp src net.jthoenes.blog.spike.lambda.LambdaIntro
- Hello from Java 8!
注意:我這里是用命令行來編譯和執(zhí)行的,因為IDE現(xiàn)在還不支持Java 8。
非Lambda的方式
舉這么一個例子,假設我想要遍歷一個對象的列表。不過由于我的業(yè)務需求,我還需要取得列表項的值和索引。如果用現(xiàn)在版本的Java來做的話,我需要把實際的邏輯和索引放在一起進行處理:
- List list = Arrays.asList("A", "B", "C");
- for (int index = 0; index < list.size(); index++) { String value = list.get(index); String output = String.format("%d -> %s", index, value);
- System.out.println(output);
- }
這樣會輸出
- 0 -> A
- 1 -> B
- 2 -> C
這樣其實也并不壞,但是我這幾行代碼里做了兩件事:控制列表的迭代以及進行了一些(簡單)的業(yè)務邏輯處理。不過如果使用Lambda表達式的話,它可以幫助我把這兩者分開進行處理。
eachWithIndex方法簽名
因此,我想實現(xiàn)一個eachWithIndex方法,它可以這樣被調用:
- List list = Arrays.asList("A", "B", "C");
- eachWithIndex(list,
- (value, index) -> {
- String output = String.format("%d -> %s", index, value);
- System.out.println(output);
- }
- );
這個方法接收兩個參數(shù)。***個參數(shù)是要處理的列表,第二個參數(shù)是一個lambda表達式或者閉包,它表示處理每個列表項的方法。你可以在第3行看到,這個lambda表達式接受兩個參數(shù):當前值和當前索引。這兩個參數(shù)都沒有類型聲明。Java 8 的編譯器會自動推導出參數(shù)的類型。在參數(shù)的后面,是一個->符號以及處理每個列表項的代碼塊。
注意:你需要在一個文本編輯器里編寫這個方法或者你需要忽略IDE提示的錯誤信息。
實現(xiàn)eachWithIndex方法
為了使用Java 8 的lambda,你需要聲明一個功能接口。功能接口是一種特殊的接口,它只有一個方法——這個方法會被lambda表達式實現(xiàn)。在這個示例里,我需要聲明一個接收一個元素和索引并且沒有返回值的接口。因此,我定義了如下的接口:
- public static interface ItemWithIndexVisitor<E> {
- public void visit(E item, int index);
- }
通過這個接口,我現(xiàn)在可以實現(xiàn)eachWithIndex方法。
- public static <E> void eachWithIndex(List<E> list, ItemWithIndexVisitor<E> visitor) {
- for (int i = 0; i < list.size(); i++) {
- visitor.visit(list.get(i), i);
- }
- }
這個方法使用了泛型參數(shù)<E>,因此傳入到visit方法里的元素類型會根據(jù)列表的類型進行推導。
使用功能接口的一個好處是,Java里已經(jīng)有了很多的功能接口。想想關于 java.util.concurrent.Callableinterface的例子。它可以被當作lambda表達式來使用,并且不需要修改 Callable的調用者的代碼。這樣使得大部分的JDK和框架默認都能夠支持lambda表達式。
使用方法引用
另外一個來自于Lambda項目的很有用的東西是方法引用。它是一種復用已有的方法并且把它們打包成一個功能接口對象的方式。假設我們有如下的代碼:
- public static <E> void printItem(E value, int index) {
- String output = String.format("%d -> %s", index, value);
- System.out.println(output);
- }
并且我想在eachWithIndex方法里調用它,那么我就可以在我的方法調用里使用::符號:
- eachWithIndex(list, LambdaIntro::printItem);
看起來很不錯,并且也很簡潔,對嗎?
總結
這樣就可以使得我的***個lambda表達式例子能夠運行。等待了這么長的時間,能夠看到閉包運行在我的Java程序里,我實在是太興奮了。Lambda表達式現(xiàn)在只是在開發(fā)者預覽的版本上可用。如果你想要了解更多的話,你可以去閱讀一下關于Lambda表達式的早期預案的評議,或者你也可以去看看Lambda項目的頁面。
我把整個實例代碼都上傳到gist上了。
英文原文:tataryn