Java8的新特性之一,就是首次引入了函數式編程Lambda表達式,按oracle的說法,是爲了引導java向函數式編程的方向發展。java
在JDK1.8中,多了一個包,java.util.function,這裏主要用到了這個包下面的兩個接口:編程
Consumer<T> //Represents an operation that accepts a single input argument and returns no result. Function<T,R> //Represents a function that accepts one argument and produces a result.
要解釋清楚這個問題,首先得從lambda表達式提及oracle
(x,y)->doSomethingWith(x,y);app
這句話就是一個lambda表達式的例子;"->"是Java8新定義的一個操做符,操做符左邊表明lambda表達式接收的參數,這裏它接收了兩個參數,x和y;表達式右邊是函數操做,也就是對這兩個變量執行某種操做。如x+y,x*y等。函數式編程
簡單的解釋了一下java8的lambda表達式,接下來進入正題:函數
在java8中,function包下面的全部接口都描述了一種預約義的lambda表達式類型,換句話說,就是能夠經過聲名接口類型的變量爲lambda賦值,從而達到函數參數化的目的,這樣說可能比較抽象,看代碼指針
public class LambdaDemo { public static void adapter(Function<String, String> function) { String message = "Hello World"; System.out.println(function.apply(message)); } public static void main(String[] args) { Function<String, String> function1 = (str) -> { return str.toUpperCase(); }; Function<String, String> function2 = (str) -> { return str.toLowerCase(); }; adapter(function1); adapter(function2); } }
仔細體會上面的代碼,函數adaper這裏表示對字符串進行某種適配並將它打印出來,而具體的適配方式是經過參數傳過來的,咱們來看看運行結果:code
HELLO WORLD hello world
和預期的徹底同樣,若是你能看懂上面的代碼,我相信你已經基本明白了java8的函數式接口用法。orm
下面我針對上面提到的兩個接口作一下解釋:接口
全部的接口都是泛型定義的,泛型的做用在於類型推斷,也就是說你指定了lambda的類型,那麼他接收的參數的類型就是肯定的,編譯器就能夠推斷lambda的類型。事實上,在「->」運算符左邊括號內的參數都是「匿名」的,你既無需考慮它們的引用,也無需事先聲名它們,它們只在當前lambda表達式內做用,而且類型已經肯定。再深刻思考一點,若是你熟悉接口重載,你可能以爲這和泛型同樣,是一塊語法糖,事實上並不是如此,Oracle爲了引導java向函數式編程的方向發展,放棄了簡單的接口重載,而是經過動態調用(invokeddynamic:https://stackoverflow.com/questions/30002380/why-are-java-8-lambdas-invoked-using-invokedynamic)實現的。
再說說這兩個接口的區別:
Consumer 中文譯做「消費者」,它經過接口下的accept方法,接收惟一的參數,並執行操做;參數和調用該方法的上下文是無關的,也就是說對變量執行的操做不影響原上下文中的變量;
Function 接口則能夠經過調用apply方法返回一個值,從而供以後調用。
要解釋清楚這一問題,還得靠代碼:
public class ContextDemo { public static String transform(String str) { return str.concat(" World"); } public static void main(String[] args) { Function<String, String> function = (str) -> { return transform(str); }; Consumer<String> consumer = (str) -> { str = transform(str); }; String msg = "Hello"; System.out.println(msg); consumer.accept(msg); System.out.println(msg); msg = function.apply(msg); System.out.println(msg); } }
運行結果:
Hello Hello Hello World
可見,Consumer並無影響到它的上下文,它用的參數是變量的「副本」;而不是變量的指針。
接下來講說相似js中的callback();
對於一項功能,若是咱們可以提供多個參數,咱們傾向於使用函數或者方法來實現;可是若是咱們須要用到多個參數,因爲函數至多隻有一個返回值,因此此時採用方法的思想咱們須要多個函數或者方法,這時最簡單的就是將方法傳過去,而不用返回,相似的場景在JS中很是常見
function doSomething(callback){ var a = 1; var b = 2; callback(a,b); } doSomething(function(a,b){ alert(a + b); });
jQuery Ajax方法中success場景下的data就是一個典型的callback,如今java也能夠實現相似的效果,從而提升代碼重用率
public class CallbackDemo { public static void main(String[] args) { sayHello((msg) -> { System.out.println(msg); }); sayHello((msg) -> { System.out.println(msg.toUpperCase()); }); sayHello((msg) -> { System.out.println(msg.replaceAll("o", "0")); }); } public static void sayHello(Consumer<String> callback) { String msg = "Hello World"; callback.accept(msg); } }
運行結果以下:
Hello World HELLO WORLD Hell0 W0rld
初來乍到,有錯誤還望批評指正。