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

jopen 8年前發布 | 8K 次閱讀 Java開發

Java8的新特性之一,就是首次引入了函數式編程Lambda表達式,按oracle的說法,是為了引導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表達式說起

(x,y)->doSomethingWith(x,y);

這句話就是一個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這里表示對字符串進行某種適配并將它打印出來,而具體的適配方式是通過參數傳過來的,我們來看看運行結果:

HELLO WORLD
hello world

和預期的完全一樣,如果你能看懂上面的代碼,我相信你已經基本明白了java8的函數式接口用法。

下面我針對上面提到的兩個接口做一下解釋:

所有的接口都是泛型定義的,泛型的作用在于類型推斷,也就是說你指定了lambda的類型,那么他接收的參數的類型就是確定的,編譯器就可以推斷lambda的類型。事實上,在“->”運算符左邊括號內的參數都是“匿名”的,你既無需考慮它們的引用,也無需事先聲名它們,它們只在當前lambda表達式內作用,并且類型已經確定。再深入思考一點,如果你熟悉接口重載,你可能覺得這和泛型一樣,是一塊語法糖,事實上并非如此,Oracle為了引導java向函數式編程的方向發展,放棄了簡單的接口重載,而是通過動態編譯實現的。

再說說這兩個接口的區別:

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

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

來自: http://my.oschina.net/u/2541538/blog/538400

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!