這裡來講解一下Java8 新特性中的函數式介面, 以及和Lambda 表達式的關係。看到過很多不少介紹Java8特性的文章,都會介紹到函數式介面和lambda表達式,但是都是分別介紹,沒有將兩者的關係說明清楚,在這裡,把自己的理解整理如下: 一、函數式介面: 函數式介面其實本質上還是一個介面,但是它 ...
這裡來講解一下Java8 新特性中的函數式介面, 以及和Lambda 表達式的關係。看到過很多不少介紹Java8特性的文章,都會介紹到函數式介面和lambda表達式,但是都是分別介紹,沒有將兩者的關係說明清楚,在這裡,把自己的理解整理如下:
一、函數式介面:
函數式介面其實本質上還是一個介面,但是它是一種特殊的介面:SAM類型的介面(Single Abstract Method)。定義了這種類型的介面,使得以其為參數的方法,可以在調用時,使用一個lambda表達式作為參數。從另一個方面說,一旦我們調用某方法,可以傳入lambda表達式作為參數,則這個方法的參數類型,必定是一個函數式的介面,這個類型必定會使用@FunctionalInterface進行修飾。
從SAM原則上講,這個介面中,只能有一個函數需要被實現,但是也可以有如下例外:
1. 預設方法與靜態方法並不影響函數式介面的契約,可以任意使用,即
函數式介面中可以有靜態方法,一個或者多個靜態方法不會影響SAM介面成為函數式介面,並且靜態方法可以提供方法實現
可以由 default 修飾的預設方法方法,這個關鍵字是Java8中新增的,為的目的就是使得某一些介面,原則上只有一個方法被實現,但是由於歷史原因,不得不加入一些方法來相容整個JDK中的API,所以就需要使用default關鍵字來定義這樣的方法
2. 可以有 Object 中覆蓋的方法,也就是 equals,toString,hashcode等方法。
JDK中以前所有的函數式介面都已經使用 @FunctionalInterface 定義,可以通過查看JDK源碼來確認,以下附JDK 8之前已有的函數式介面:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
如:
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
以下為一個自定義函數式介面的示例:
定義:
@FunctionalInterface interface Converter<F, T> { T convert(F from); }
使用:
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
註:方法和構造函數引用在Java8中可以通過 :: 操作符調用
自行設計的方法中, 如果可以接收 lambda 表達式, 則可以使用 Function 作為參數, 如下為一些已經實現的函數式介面:
// Function<T, R> -T作為輸入,返回的R作為輸出 Function<String,String> function = (x) -> {System.out.print(x+": ");return "Function";}; System.out.println(function.apply("hello world")); //Predicate<T> -T作為輸入,返回的boolean值作為輸出 Predicate<String> pre = (x) ->{System.out.print(x);return false;}; System.out.println(": "+pre.test("hello World")); //Consumer<T> - T作為輸入,執行某種動作但沒有返回值 Consumer<String> con = (x) -> {System.out.println(x);}; con.accept("hello world"); //Supplier<T> - 沒有任何輸入,返回T Supplier<String> supp = () -> {return "Supplier";}; System.out.println(supp.get()); //BinaryOperator<T> -兩個T作為輸入,返回一個T作為輸出,對於“reduce”操作很有用 BinaryOperator<String> bina = (x,y) ->{System.out.print(x+" "+y);return "BinaryOperator";}; System.out.println(" "+bina.apply("hello ","world"));
二、Lambda表達式(這裡只是簡單提一下)
書寫方法: e -> System.out.println( e )
1. 三部分構成
參數列表
符號 ->
函數體 : 有多個語句,可以用{} 包括, 如果需要返回值且只有一個語句,可以省略 return
2. 訪問控制:
可以訪問類的成員變數和局部變數(非final會自動隱含轉為final)
以上主要是對於函數式介面的一些簡單介紹,如有疏漏,歡迎指正