允許在接口中有默認方法實現(xiàn)
Java 8 允許我們使用default關鍵字,為接口聲明添加非抽象的方法實現(xiàn)。這個特性又被稱為擴展方法。下面是我們的第一個例子:
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
在接口Formula中,除了抽象方法caculate以外,還定義了一個默認方法sqrt。Formula的實現(xiàn)類只需要實現(xiàn)抽象方法caculate就可以了。默認方法sqrt可以直接使用。
Formula formula = new Formula() {
@Override
public double calculate(int a) {
return sqrt(a * 100);
}
};
formula.calculate(100); // 100.0
formula.sqrt(16); // 4.0
formula對象以匿名對象的形式實現(xiàn)了Formula接口。代碼很啰嗦:用了6行代碼才實現(xiàn)了一個簡單的計算功能:a*100開平方根。我們在下一節(jié)會看到,Java 8 還有一種更加優(yōu)美的方法,能夠實現(xiàn)包含單個函數(shù)的對象。
Lambda表達式
讓我們從最簡單的例子開始,來學習如何對一個string列表進行排序。我們首先使用Java 8之前的方法來實現(xià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接受一個list,和一個Comparator接口作為輸入?yún)?shù),Comparator的實現(xiàn)類可以對輸入的list中的元素進行比較。通常情況下,你可以直接用創(chuàng)建匿名Comparator對象,并把它作為參數(shù)傳遞給sort方法。
除了創(chuàng)建匿名對象以外,Java 8 還提供了一種更簡潔的方式,Lambda表達式。
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
你可以看到,這段代碼就比之前的更加簡短和易讀。但是,它還可以更加簡短:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
只要一行代碼,包含了方法體。你甚至可以連大括號對{}和return關鍵字都省略不要。不過這還不是最短的寫法:
Collections.sort(names, (a, b) -> b.compareTo(a));
Java編譯器能夠自動識別參數(shù)的類型,所以你就可以省略掉類型不寫。讓我們再深入地研究一下lambda表達式的威力吧。
函數(shù)式接口
Lambda表達式如何匹配Java的類型系統(tǒng)?每一個lambda都能夠通過一個特定的接口,與一個給定的類型進行匹配。一個所謂的函數(shù)式接口必須要有且僅有一個抽象方法聲明。每個與之對應的lambda表達式必須要與抽象方法的聲明相匹配。由于默認方法不是抽象的,因此你可以在你的函數(shù)式接口里任意添加默認方法。
任意只包含一個抽象方法的接口,我們都可以用來做成lambda表達式。為了讓你定義的接口滿足要求,你應當在接口前加上@FunctionalInterface 標注。編譯器會注意到這個標注,如果你的接口中定義了第二個抽象方法的話,編譯器會拋出異常。
舉例:
@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 標注,程序也是正確的。
方法和構造函數(shù)引用
上面的代碼實例可以通過靜態(tài)方法引用,使之更加簡潔:
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted); // 123
Java 8 允許你通過::關鍵字獲取方法或者構造函數(shù)的的引用。上面的例子就演示了如何引用一個靜態(tài)方法。而且,我們還可以對一個對象的方法進行引用:
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"
讓我們看看如何使用::關鍵字引用構造函數(shù)。首先我們定義一個示例bean,包含不同的構造方法:
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
接下來,我們定義一個person工廠接口,用來創(chuàng)建新的person對象:
interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
}
然后我們通過構造函數(shù)引用來把所有東西拼到一起,而不是像以前一樣,通過手動實現(xiàn)一個工廠來這么做。
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
我們通過Person::new來創(chuàng)建一個Person類構造函數(shù)的引用。Java編譯器會自動地選擇合適的構造函數(shù)來匹配PersonFactory.create函數(shù)的簽名,并選擇正確的構造函數(shù)形式。