為忙碌的程序員歸納總結(jié)和翻譯的Java8極簡(jiǎn)教程,有助于快速了解Java8的新特性。
系列文章鏈接:
Java8極簡(jiǎn)手冊(cè)-01
Java8極簡(jiǎn)手冊(cè)-02
Java8極簡(jiǎn)手冊(cè)-03
Java8極簡(jiǎn)手冊(cè)-04
接口的默認(rèn)方法(Default Methods for Interfaces)
Java 8允許我們利用default關(guān)鍵字在接口中定義非抽象的方法實(shí)現(xiàn),這個(gè)功能也被稱為虛擬擴(kuò)展方法( virtual extension methods)。
以下是第一個(gè)例子:
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
除了抽象方法(可以理解為無(wú)具體實(shí)現(xiàn)的方法) calculate之外,接口 Formula 里面還定了一個(gè)默認(rèn)方法 sqrt。在具體的實(shí)現(xiàn)類里面,抽象方法 calculate需要被重新>實(shí)現(xiàn),而默認(rèn)方法sqrt則可以被實(shí)現(xiàn)類直接使用。
Formula formula = new Formula() {
@Override
public double calculate(int a) {
return sqrt(a * 100);
}
};
formula.calculate(100); // 100.0
formula.sqrt(16); // 4.0
該公式是作為匿名對(duì)象實(shí)現(xiàn)的,代碼或許過(guò)于冗長(zhǎng):6行代碼實(shí)現(xiàn)的僅僅是一個(gè)簡(jiǎn)單的計(jì)算 sqrt(a * 100),下一節(jié)我們將看到在Java8中是怎么樣更優(yōu)雅地實(shí)現(xiàn)這樣的單方法對(duì)象的。
Lambda表達(dá)式
讓我們從一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明如何在以前版本的Java中對(duì)字符串列表進(jìn)行排序:
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è)comparator,以便對(duì)給定list的元素進(jìn)行排序。 很多時(shí)候我們會(huì)發(fā)現(xiàn)需要先創(chuàng)建匿名comparator,并將它們傳遞給排序方法。
從Java 8開(kāi)始,我們不需要整天創(chuàng)建匿名對(duì)象了,而是可以使用更短的語(yǔ)法 :lambda expressions(lambda表達(dá)式):
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
你可以看見(jiàn),代碼更精簡(jiǎn),而且更具可讀性了(注:可讀性是對(duì)于熟悉lambda的開(kāi)發(fā)者來(lái)說(shuō)的),而且,我們還可以把代碼寫(xiě)的更精簡(jiǎn):
Collections.sort(names, (String a, String b) -> b.compareTo(a));
對(duì)于一行方法體,您可以跳過(guò)大括號(hào){}和return關(guān)鍵字, 所以還可以把它變得更短:
names.sort((a, b) -> b.compareTo(a));
列表現(xiàn)在有了一個(gè)排序方法。 此外,java編譯器會(huì)知道參數(shù)類型,所以你可以跳過(guò)它們。 讓我們深入了解如何在更多的場(chǎng)景中使用lambda表達(dá)式。
函數(shù)式接口(Functional Interfaces)
Lambda表達(dá)式如何匹配識(shí)別Java的類型系統(tǒng)?
每個(gè)lambda對(duì)應(yīng)于由接口指定的給定類型,一個(gè)所謂的函數(shù)式接口必須包含一個(gè)精確的抽象方法聲明,該類型的每個(gè)lambda表達(dá)式都將與此抽象方法匹配。 由于默認(rèn)方法不
是抽象的,你可以自由地將默認(rèn)方法添加到你的函數(shù)式接口。
只要接口滿足只包含一個(gè)抽象方法的約定,我們就可以使用任意接口作為lambda表達(dá)式。 為了確保你的接口符合要求,你應(yīng)該添加@FunctionalInterface注解。 編譯器知道這>個(gè)注釋,并會(huì)在你嘗試向接口添加第二個(gè)抽象方法聲明后立即拋出編譯器錯(cuò)誤。
例子:
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted); // 123
需要注意的是,如果@FunctionalInterface注釋被省略掉,代碼也是有效的。
方法和構(gòu)造器引用
The above example code can be further simplified by utilizing static method references:
上面的示例代碼可以通過(guò)使用靜態(tài)方法引用進(jìn)一步簡(jiǎn)化:
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted); // 123
Java 8讓您能夠通過(guò)::關(guān)鍵字傳遞方法或構(gòu)造函數(shù)的引用。 上面的例子展示了如何引用一個(gè)靜態(tài)方法。 但是我們也可以引用對(duì)象的方法:
class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted); // "J"
Let's see how the :: keyword works for constructors. First we define an example class with different constructors:
我們來(lái)看看::關(guān)鍵字如何用于構(gòu)造函數(shù),首先我們用不同的構(gòu)造函數(shù)定義一個(gè)示例類:
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
接下來(lái),我們指定一個(gè)用于創(chuàng)建新的人員的Person工廠接口:
interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
}
我們不是手動(dòng)實(shí)現(xiàn)工廠,而是通過(guò)構(gòu)造函數(shù)引用將所有東西粘合在一起:
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
我們通過(guò)Person :: new創(chuàng)建一個(gè)對(duì)Person構(gòu)造函數(shù)的引用。 Java編譯器通過(guò)匹配PersonFactory.create的參數(shù)和類型自動(dòng)選擇正確的構(gòu)造函數(shù)。