1.Lambda 表達(dá)式
首先看看在老版本的Java中是如何排列字符串的:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
只需要給靜態(tài)方法 Collections.sort 傳入一個(gè)List對象以及一個(gè)比較器來按指定順序排列。通常做法都是創(chuàng)建一個(gè)匿名的比較器對象然后將其傳遞給sort方法裆蒸。
在Java 8 中你就沒必要使用這種傳統(tǒng)的匿名對象的方式了,Java 8提供了更簡潔的語法佛致,lambda表達(dá)式:
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
看到了吧辙谜,代碼變得更段且更具有可讀性,但是實(shí)際上還可以寫得更短:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
對于函數(shù)體只有一行代碼的筷弦,你可以去掉大括號{}以及return關(guān)鍵字抑诸,但是你還可以寫得更短點(diǎn):
Collections.sort(names, (a, b) -> b.compareTo(a));
接下來我們看看lambda表達(dá)式還能作出什么更方便的東西來:
2爹殊、函數(shù)式接口
Lambda 表達(dá)式是如何在java的類型系統(tǒng)中表示的呢?每一個(gè)lambda表達(dá)式都對應(yīng)一個(gè)類型层玲,通常是接口類型反症。而“函數(shù)式接口”是指僅僅只包含一個(gè)抽象方法的 接口,每一個(gè)該類型的lambda表達(dá)式都會(huì)被匹配到這個(gè)抽象方法铅碍。因?yàn)?默認(rèn)方法 不算抽象方法,所以你也可以給你的函數(shù)式接口添加默認(rèn)方法尘盼。
我們可以將lambda表達(dá)式當(dāng)作任意只包含一個(gè)抽象方法的接口類型烦绳,確保你的接口一定達(dá)到這個(gè)要求,你只需要給你的接口添加 @FunctionalInterface 注解径密,編譯器如果發(fā)現(xiàn)你標(biāo)注了這個(gè)注解的接口有多于一個(gè)抽象方法的時(shí)候會(huì)報(bào)錯(cuò)的。
示例如下:
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
驗(yàn)證: Converter<String, Integer> converter = new Converter<String, Integer()
{
@Override
public Integer converter(String form) {
return Integer.valueOf(form);
}
};
// 簡化
converter = (String form) -> {return Integer.valueOf(form);};
// 簡化
converter = (form) -> Integer.valueOf(form);
Integer integer = converter.converter("123");
System.out.println(integer);
需要注意如果@FunctionalInterface如果沒有指定底桂,上面的代碼也是對的伪很。
3.方法與構(gòu)造函數(shù)引用
前一節(jié)中的代碼還可以通過靜態(tài)方法引用來表示:
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted); // 123
Java 8 允許你使用 :: 關(guān)鍵字來傳遞方法或者構(gòu)造函數(shù)引用,上面的代碼展示了如何引用一個(gè)靜態(tài)方法猫十,我們也可以引用一個(gè)對象的方法:
String something = "JavaEE";
Converter<String, Boolean> converter1 = something::startsWith;
boolean res = converter1.converter("Java");
System.out.println(res);
接下來看看構(gòu)造函數(shù)是如何使用::關(guān)鍵字來引用的呆盖,首先我們定義一個(gè)包含多個(gè)構(gòu)造函數(shù)的簡單類:
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
接下來我們指定一個(gè)用來創(chuàng)建Person對象的對象工廠接口:
interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
}
這里我們使用構(gòu)造函數(shù)引用來將他們關(guān)聯(lián)起來,而不是實(shí)現(xiàn)一個(gè)完整的工廠:
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
我們只需要使用 Person::new 來獲取Person類構(gòu)造函數(shù)的引用宙项,Java編譯器會(huì)自動(dòng)根據(jù)PersonFactory.create方法的簽名來選擇合適的構(gòu)造函數(shù)株扛。
四汇荐、Lambda 作用域
在lambda表達(dá)式中訪問外層作用域和老版本的匿名對象中的方式很相似。你可以直接訪問標(biāo)記了final的外層局部變量掀淘,或者實(shí)例的字段以及靜態(tài)變量油昂。
五、訪問局部變量
我們可以直接在lambda表達(dá)式中訪問外層的局部變量:
復(fù)制代碼 代碼如下:
final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
但是和匿名對象不同的是冕碟,這里的變量num可以不用聲明為final,該代碼同樣正確:
復(fù)制代碼 代碼如下:
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
不過這里的num必須不可被后面的代碼修改(即隱性的具有final的語義)厕妖,例如下面的就無法編譯:
復(fù)制代碼 代碼如下:
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
num = 3;
在lambda表達(dá)式中試圖修改num同樣是不允許的挑庶。
六、訪問對象字段與靜態(tài)變量
和本地變量不同的是,lambda內(nèi)部對于實(shí)例的字段以及靜態(tài)變量是即可讀又可寫埋嵌。該行為和匿名對象是一致的:
復(fù)制代碼 代碼如下:
class Lambda4 {
static int outerStaticNum;
int outerNum;
void testScopes() {
Converter<Integer, String> stringConverter1 = (from) -> {
outerNum = 23;
return String.valueOf(from);
};
Converter<Integer, String> stringConverter2 = (from) -> {
outerStaticNum = 72;
return String.valueOf(from);
};
}
}