寫在前面: 最近在看<<Java8實戰>>,感受這本書的排版,紙質,內容真的都超級棒,我的以爲這本書仍是很值得一讀.本文簡單或詳細介紹一下Java8的FunctionalInterface和Lambda表達式.java
函數式接口(Functional Interface)是在Java8纔開始引入的.首先看一下Java普通接口和函數式接口的區別:express
1.若是定義的是一個泛型的函數式接口的話,好比:ide
@FunctionalInterface public interface MyFunction1<T, R> { R calculate(T t); }
那麼全部只有一個參數並有返回值的函數都是MyFunction1的一個實例.也就是說這些方法都是類似的方法.函數
2.若是定義的是一個具體的函數式接口,好比:code
@FunctionalInterface public interface MyFunction2 { void print(Integer i); }
那麼全部參數是Integer類型的而且沒有返回值的函數都是MyFunction2的一個實例.也就是說這些方法都是類似的方法.對象
Java的接口能夠當作函數的參數,那麼函數式接口天然也能夠,這就叫作行爲參數化.接口
在Java8中已經定義了好多的函數式接口,好比:get
那麼定義一個函數式接口,有哪些須要注意的地方呢?it
@FunctionalInterface
註解.對於@FunctionalInterface
註解,其實不加也能夠,徹底是能夠經過編譯的,但前提是這個接口須要知足上面的第二個注意點.io
但又是爲何須要@FunctionalInterface
?首先咱們先看一個例子:
假設咱們定義了一個函數式接口,可是忘記了添加了
@FunctionInterface
,同時這個函數式接口還沒被其餘地方引用,若是這個時候咱們又添加了一個抽象方法,是不會報錯的.由於這個接口有了多個抽象方法,因此這個接口就是一個普通的接口,而不是咱們所指望的函數式接口.
這時候,若是添加了@FunctionalInterface
註解的話,就會報錯,提示說這個接口具備多個抽象方法.實際上,我的理解@FunctionalInterface
就是手動添加約束,說明這個接口就是函數式接口,只能有一個抽象方法.
接下來編寫並使用一個自定義函數式接口
// define a functional interface @FunctionalInterface public interface MyFunction<T, R> { R convert(T t); } // use a function interface public class Test { public static void main(String[] args) { MyFunction<Long, String> f = (Long l) -> String.valueOf(l); System.out.println(f.convert(10L)); // f: convert a Long to String MyFunction<String, Long> g = (String s) -> Long.parseLong(s); System.out.println(g.convert("10")); // g: convert a String to Long } }
我們能夠看一看<<Java8實戰>>這本書中的定義:
能夠把Lambda表達式理解爲簡潔地表示可傳遞的匿名函數的一種方式: 它沒有名稱,但它有參數列表,函數主體,返回類型,可能還有一個能夠拋出的異常列表.
- 匿名: 是由於它不像普通的方法有一個明確的名稱
- 函數: 是由於Lambda函數不像方法那樣屬於某個特定的類,但又和方法同樣,有參數列表,函數主體,返回值類型,還可能有能夠拋出的異常列表
- 傳遞: Lambda表達式能夠做爲參數傳遞給方法或存儲在變量中
- 簡潔: 無須像匿名類那樣寫不少模板代碼
我的理解,Lambda表達式徹底是服務於函數式接口的,就是爲了在建立一個函數式接口實例時更加的直觀,簡潔.好比我們要建立一個匿名類對象時,能夠看一下普通接口和函數式接口的區別:
// a common interface public interface Common { void f(); void g(); } @FunctionalInterface public interface MyFunction<T, R> { R convert(T); } public class Test { public static void main(String[] args){ // create a instance of common interface Common c = new Common() { @Override public void f() {} @Override public void g() {} }; // create a instance of functional interface MyFunction<Long, String> f = (Long l) -> String.valueOf(l); } }
提及方法引用以前,我們先看一個例子
// 1 MyFunction<Long, String> f = (Long l) -> String.valueOf(l); // 2: 經過Lambda的上下文推斷出l的數據類型 MyFunction<Long, String> f = l -> String.valueOf(l); // // 3 MyFunction<Long, String> f = String::valueOf;
第一種,第二種和第三種寫法的做用徹底是同樣的,但顯然第三種的寫法更加簡潔.形如xxx::xxx這樣的表達式就是方法引用.方法引用能夠被看作僅僅調用特定方法的Lambda的一種快捷寫法.
總共有四種方法引用,下面我們根據一個實例來講明並解釋這幾種方法引用.
// common class public class FunctionInstance { public Integer getLength(String str) { return Optional.ofNullable(str).orElse("").length(); // Optional也是Java8引入的 } } // test class public class MethodReference { public static void main(String[] args) { // 1.指向靜態方法的方法引用 //Function<String, Integer> f1 = s -> Integer.parseInt(s); Function<String, Integer> f1 = Integer::parseInt; // 2.指向任意類型實例方法的方法引用,s爲內部對象 //Predicate<String> f2 = s -> s.isEmpty(); Predicate<String> f2 = String::isEmpty; // 3.指向現有對象(外部對象)的實例方法的方法引用, instance爲外部對象 //Function<String, Integer> f3 = s -> instance.getLength(s); FunctionInstance instance = new FunctionInstance(); Function<String, Integer> f3 = instance::getLength; // 4.構造函數引用 //Supplier<MethodReference> f4 = () -> new MethodReference(); Supplier<MethodReference> f4 = MethodReference::new; } }
2和3有人可能會混淆, 反正一開始看的時候我是沒區分出來.其實就看參數是爲內部對象仍是外部對象.內部對象就是用2,外部對象就是用3
Lambda表達式
中的對象,好比上面的s, 傳入的參數,屬於內部對象Lambda表達式
外的對象,好比上面的instance函數式接口: 就是一個特殊的接口,只能有一個抽象方法.因此能夠將函數式接口想象成是一個函數類型(好比Predicate,Consumer或自定義的函數式接口).
Lambda表達式: 則是簡化了匿名類對象的建立.正如我在上文所說的,Lambda表達式徹底是服務於函數式接口的,正是由於了函數式接口的特殊性,因此才使用了Lambda表達式來建立一個函數類型實例,而不是像匿名類那樣寫一堆無用的模板代碼.