Java 8簡明教程
“Java並沒有沒落,人們很快就會發現這一點”
歡迎閱讀我編寫的Java 8介紹。本教程將帶領你一步一步地認識這門語言的新特性。通過簡單明瞭的程式碼示例,你將會學習到如何使用預設介面方法,Lambda表示式,方法引用和重複註解。看完這篇教程後,你還將對最新推出的API有一定的瞭解,例如:流控制,函式式介面,map擴充套件和新的時間日期API等等。
允許在介面中有預設方法實現
Java 8 允許我們使用default關鍵字,為介面宣告新增非抽象的方法實現。這個特性又被稱為擴充套件方法。下面是我們的第一個例子:
1 2 3 4 5 6 7 |
interface Formula {
double calculate( int a);
default double sqrt( int a) {
return Math.sqrt(a);
}
}
|
在介面Formula中,除了抽象方法caculate以外,還定義了一個預設方法sqrt。Formula的實現類只需要實現抽象方法caculate就可以了。預設方法sqrt可以直接使用。
1 2 3 4 5 6 7 8 9 |
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物件以匿名物件的形式實現了Formula介面。程式碼很囉嗦:用了6行程式碼才實現了一個簡單的計算功能:a*100開平方根。我們在下一節會看到,Java 8 還有一種更加優美的方法,能夠實現包含單個函式的物件。
Lambda表示式
讓我們從最簡單的例子開始,來學習如何對一個string列表進行排序。我們首先使用Java 8之前的方法來實現:
1 2 3 4 5 6 7 8 |
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);
}
});
|
靜態工具方法Collections.sort接受一個list,和一個Comparator介面作為輸入引數,Comparator的實現類可以對輸入的list中的元素進行比較。通常情況下,你可以直接用建立匿名Comparator物件,並把它作為引數傳遞給sort方法。
除了建立匿名物件以外,Java 8 還提供了一種更簡潔的方式,Lambda表示式。
1 2 3 |
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
|
你可以看到,這段程式碼就比之前的更加簡短和易讀。但是,它還可以更加簡短:
1 |
Collections.sort(names, (String a, String b) -> b.compareTo(a));
|
只要一行程式碼,包含了方法體。你甚至可以連大括號對{}和return關鍵字都省略不要。不過這還不是最短的寫法:
1 |
Collections.sort(names, (a, b) -> b.compareTo(a));
|
Java編譯器能夠自動識別引數的型別,所以你就可以省略掉型別不寫。讓我們再深入地研究一下lambda表示式的威力吧。
函式式介面
Lambda表示式如何匹配Java的型別系統?每一個lambda都能夠通過一個特定的介面,與一個給定的型別進行匹配。一個所謂的函式式介面必須要有且僅有一個抽象方法宣告。每個與之對應的lambda表示式必須要與抽象方法的宣告相匹配。由於預設方法不是抽象的,因此你可以在你的函式式接口裡任意新增預設方法。
任意只包含一個抽象方法的介面,我們都可以用來做成lambda表示式。為了讓你定義的介面滿足要求,你應當在介面前加上@FunctionalInterface 標註。編譯器會注意到這個標註,如果你的介面中定義了第二個抽象方法的話,編譯器會丟擲異常。
舉例:
1 2 3 4 5 6 7 8 |
@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 標註,程式也是正確的。
方法和建構函式引用
上面的程式碼例項可以通過靜態方法引用,使之更加簡潔:
1 2 3 |
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert( "123" );
System.out.println(converted); // 123
|
Java 8 允許你通過::關鍵字獲取方法或者建構函式的的引用。上面的例子就演示瞭如何引用一個靜態方法。而且,我們還可以對一個物件的方法進行引用:
1 2 3 4 5 6 7 8 9 10 |
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"
|
讓我們看看如何使用::關鍵字引用建構函式。首先我們定義一個示例bean,包含不同的構造方法:
1 2 3 4 5 6 7 8 9 10 11 |
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this .firstName = firstName;
this .lastName = lastName;
}
}
|
接下來,我們定義一個person工廠介面,用來建立新的person物件: