Java 8從 2014/3/18 正式推出至今,也一個多月了,而Eclipse也在Java 8推出後宣布Eclipse 4.3.2將會支援Java8,因此花點時間測試了一下最有興趣的新特性,也就是本篇的標題,Lambda Expressions。
環境準備:
要用Eclipse來測試Java 8,需要先準備好Java 8相關的開發工具。
Java Platform (JDK) 8u5,此為Java的開發工具,目前版本為8u5。
eclipse 4.3.2(kepler SR2),Eclipse要4.3.2版才會支援Java 8,由於我是單純要測試而已,因此只抓標準版的Eclipse即可。
Eclipse JDT升級,才能將專案的compiler設為1.8。安裝完Eclipse 4.3.2之後,再到:
Help -> Eclipse Marketplace,搜尋Java 8,安裝Java 8 support for Eclipse Kepler SR2。
- 開一個新的project,並將project設定為 compiler 1.8。
做完以上的準備,可以開始測試Java 8了。
Lambda語法(Syntax)介紹:
Lambda語法架構為:
Argument List | Arrow Token | Body |
---|---|---|
(int x, int y) | -> | x+y |
下面有幾個範例,來對Lambda有基本的認識:
輸入沒有任何參數,輸出2:
() -> 2;
輸入兩個int參數,輸出兩個參數相加的結果:
(int x, int y) -> x+y;
輸入字串,在console顯示輸入字串:
(String s) -> System.out.println(s);
Functional Interfaces:
對於Lambda的語法有了了解之後,接著來看看Java 8如何使用它。Java 8引進的一個新的詞彙,Functional Interfaces,指的是只擁有單一抽象方法的介面。這詞彙並不是新概念,我們之前在開發時也常常會使用到類似的介面,如比較資料時會用到的Comparator介面,或是在跑執行緒時會用到的Runnable介面。
而只要是Functional Interfaces,就可以用Lambda的方式來實作!比如說Runnable介面,原本可以透過匿名函式的方式實作,現在可改用Lambda的方式來實作,如下:
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("run!");
}
};
Runnable r2 = () -> System.out.println("run!");
再來看看Comparator介面,用匿名函式與用Lambda的實作方式:
Comparator<Integer> c1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
Comparator<Integer> c2 = (o1, o2) -> o1.compareTo(o2);
透過Lambda來實作可以讓程式可以精簡很多,並增加程式的可讀性。
另外,Java 8也引進了新的標注(Annotation),來標記介面為Functional Interface,只要該介面有兩個以上的自定義方法,則編譯器會報錯,此標注讓團隊的其他開發人員不會在此介面上加上別的方法。
@FunctionalInterface
interface Ti {
public void test(String s);
}
java.util.function:
Java的型別需要事先定義好,程式才能進行編譯。因此為了搭配Lambda語法,Java 8提供新的介面讓人使用,這些介面統一放在java.util.function底下,每個介面都代表一個方法,根據輸入參數與輸出參數,定了多個介面出來。以下介紹幾個基本類型的介面:
- Consumer< T >:輸入參數類型為T,沒有任何輸出。
- Function< T, R >:輸入參數類型為T,輸出參數類型為R。
- Predicate < T >:輸入參數類型為T,輸出一個布林值。
- Supplier< T >:沒有任何輸入,輸出參數類型為T。
- UnaryOperator< T >:輸入參數類型為T,輸出參數類型為T。
舉例來說,今天我需要一個接受字串作為輸入而沒有任何輸出的方法,則我可以使用Consumer這個介面來操作,如下:
Consumer<String> c = (s) -> System.out.println(s);
c.accept("hello world!"); // output hello world!
ArrayList與java.util.function:
接著要談到於Java 8的Collections中,新增了幾個方法可以搭配Lambda使用。這邊會說明ArrayList的三個方法:
- forEach(Consumer<? super E> action)
- removeIf(Predicate<? super E> filter)
- replaceAll(UnaryOperator
operator)
首先介紹forEach方法,就跟for each語法一樣,只是這邊更直覺。傳入參數為Consumer介面,Consumer介面為單一輸入參數,沒有任何輸出參數,下面為範例:
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 7, 6, 2, 3);
// for each語法
for(Integer t : numbers) {
System.out.println(t);
}
// Lambda語法
numbers.forEach((t) -> {
System.out.println(t);
});
接著介紹removeIf方法,傳入參數為Predicate介面,此介面輸入參數類型為T,然後輸出一個布林值。也就是說,他會幫你繞行陣列的每個元素,元素會被丟到Predicate內執行,當執行結果為true,則該元素會被刪除,範例如下:
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 7, 6, 2, 3);
System.out.println(list); // output:[7, 6, 2, 3]
list.removeIf((n) -> {
if (n > 3) {
return true;
} else {
return false;
}
});
System.out.println(list); // output:[2, 3]
最後是replaceAll方法,傳入參數為UnaryOperator介面,此介面輸入參數類型和輸出參數類型是一樣的,這邊運作的方法是,回傳的參數會取代掉輸入的參數,範例如下:
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 7, 6, 2, 3);
System.out.println(list); // output:[7, 6, 2, 3]
list.replaceAll((t) -> t + 1);
System.out.println(list); // output:[8, 7, 3, 4]
Method and Constructor References:
在Java 8,你可以將將方法的參考傳給變數,摘錄JSR335的內容:
Examples of method and constructor references:
System::getProperty
"abc"::length
String::length
super::toString
ArrayList::new
int[]::new
說明一下上面的例子:
- 如果你要需要靜態方法的參考,則可以使用 類別名稱::靜態方法名稱 來得到參考。
- 如果你是需要某個實例物件的方法他的參考,則可以用 實例變數::方法名稱 來得到參考。
- 如果你需要得到的是建構式,則可以使用 類別名稱::new 來得到參考。
下面為一個簡單的範例,透過Lambda實作一個介面,之後將介面的方法傳給變數,在用此變數呼叫該方法。
public static void main(String[] args) {
TestInterface ti = (i) -> (i > 5) ? true : false;
Predicate<Integer> p = ti::isGreaterThenFive;
System.out.println(p.test(6));
}
@FunctionalInterface
interface TestInterface {
public boolean isGreaterThenFive(Integer i);
}
參考:
Java Language Specification - 9.8 Functional Interfaces
Introduction to Functional Interfaces – A concept recreated in Java 8
Lambda Specification, Part C: Method and Constructor References
沒有留言:
張貼留言