利用java8新特性實現相似javascript callback特性

 

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

初來乍到,有錯誤還望批評指正。

相關文章
相關標籤/搜索