国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

Java Lambda表達(dá)式初探

Original 2016-11-07 16:35:01 394
abstract:前言本文受啟發(fā)于Trisha Gee在JavaOne 2016的主題演講Refactoring to Java 8。Java 8已經(jīng)發(fā)行兩年多,但很多人仍然在使用JDK7。對(duì)企業(yè)來(lái)說(shuō),技術(shù)上謹(jǐn)慎未必是壞事,但對(duì)個(gè)人學(xué)習(xí)而言,不去學(xué)習(xí)新技術(shù)就很可能被技術(shù)拋棄。Java 8一個(gè)重要的變更是引入Lambda表達(dá)式(lambda expression),這聽(tīng)起來(lái)似乎很牛,有種我雖然不知道Lambda表達(dá)式

前言

本文受啟發(fā)于Trisha Gee在JavaOne 2016的主題演講Refactoring to Java 8。

Java 8已經(jīng)發(fā)行兩年多,但很多人仍然在使用JDK7。對(duì)企業(yè)來(lái)說(shuō),技術(shù)上謹(jǐn)慎未必是壞事,但對(duì)個(gè)人學(xué)習(xí)而言,不去學(xué)習(xí)新技術(shù)就很可能被技術(shù)拋棄。Java 8一個(gè)重要的變更是引入Lambda表達(dá)式(lambda expression),這聽(tīng)起來(lái)似乎很牛,有種我雖然不知道Lambda表達(dá)式是什么,但我仍然覺(jué)得很厲害的感覺(jué)。不要怕,具體到語(yǔ)言層面上Lambda表達(dá)式不過(guò)是一種新的語(yǔ)法而已,有了它,Java將開(kāi)啟函數(shù)式編程的大門(mén)。

為什么需要Lambda表達(dá)式

不要糾結(jié)什么是Lambda表達(dá)式、什么是函數(shù)式編程。先來(lái)看一下Java 8新的語(yǔ)法特性帶來(lái)的便利之處,相信你會(huì)過(guò)目不忘的。

在有Lambda表達(dá)式之前,要新建一個(gè)線(xiàn)程,需要這樣寫(xiě):

new Thread(new Runnable(){
    @Override
    public void run(){
        System.out.println("Thread run()");
    }
}).start();

有Lambda表達(dá)式之后,則可以這樣寫(xiě):

new Thread(
        () -> System.out.println("Thread run()")
).start();

正如你所見(jiàn),之前無(wú)用的模板代碼不見(jiàn)了!如上所示,Lambda表達(dá)式一個(gè)常見(jiàn)的用法是取代(某些)匿名內(nèi)部類(lèi),但Lambda表達(dá)式的作用不限于此。

Lambda表達(dá)式的原理

剛接觸Lambda表達(dá)式可能覺(jué)得它很神奇:不需要聲明類(lèi)或者方法的名字,就可以直接定義函數(shù)。這看似是編譯器為匿名內(nèi)部類(lèi)簡(jiǎn)寫(xiě)提供的一個(gè)小把戲,但事實(shí)上并非如此,Lambda表達(dá)式實(shí)際上是通過(guò)invokedynamic指令來(lái)實(shí)現(xiàn)的。先別管這么多,下面是Lambda表達(dá)式幾種可能的書(shū)寫(xiě)形式,“看起來(lái)”并不是很難理解。

Runnable run = () -> System.out.println("Hello World");// 1
ActionListener listener = event -> System.out.println("button clicked");// 2
Runnable multiLine = () -> {// 3
    System.out.println("Hello ");
    System.out.println("World");
};
BinaryOperator<Long> add = (Long x, Long y) -> x + y;// 4
BinaryOperator<Long> addImplicit = (x, y) -> x + y;// 5

通過(guò)上例可以發(fā)現(xiàn):

Lambda表達(dá)式是有類(lèi)型的,賦值操作的左邊就是類(lèi)型。Lambda表達(dá)式的類(lèi)型實(shí)際上是對(duì)應(yīng)接口的類(lèi)型。

Lambda表達(dá)式可以包含多行代碼,需要用大括號(hào)把代碼塊括起來(lái),就像寫(xiě)函數(shù)體那樣。

大多數(shù)時(shí)候,Lambda表達(dá)式的參數(shù)表可以省略類(lèi)型,就像代碼2和5那樣。這得益于javac的類(lèi)型推導(dǎo)機(jī)制,編譯器可以根據(jù)上下文推導(dǎo)出類(lèi)型信息。

表面上看起來(lái)每個(gè)Lambda表達(dá)式都是原來(lái)匿名內(nèi)部類(lèi)的簡(jiǎn)寫(xiě)形式,該內(nèi)部類(lèi)實(shí)現(xiàn)了某個(gè)函數(shù)接口(Functional Interface),但事實(shí)比這稍微復(fù)雜一些,這里不再展開(kāi)。所謂函數(shù)接口是指內(nèi)部只有一個(gè)接口函數(shù)的接口。Java是強(qiáng)類(lèi)型語(yǔ)言,無(wú)論有沒(méi)有顯式指明,每個(gè)變量和對(duì)象都必須有明確的類(lèi)型,沒(méi)有顯式指定的時(shí)候編譯器會(huì)嘗試確定類(lèi)型。Lambda表達(dá)式的類(lèi)型就是對(duì)應(yīng)函數(shù)接口的類(lèi)型。

Lambda表達(dá)式和Stream

Lambda表達(dá)式的另一個(gè)重要用法,是和Stream一起使用。Stream is a sequence of elements supporting sequential and parallel aggregate operations。Stream就是一組元素的序列,支持對(duì)這些元素進(jìn)行各種操作,而這些操作是通過(guò)Lambda表達(dá)式指定的。可以把Stream看作Java Collection的一種視圖,就像迭代器是容器的一種視圖那樣(但Stream不會(huì)修改容器中的內(nèi)容)。下面例子展示了Stream的常見(jiàn)用法。

例子1

假設(shè)需要從一個(gè)字符串列表中選出以數(shù)字開(kāi)頭的字符串并輸出,Java 7之前需要這樣寫(xiě):

List<String> list = Arrays.asList("1one", "two", "three", "4four");
for(String str : list){
    if(Character.isDigit(str.charAt(0))){
        System.out.println(str);
    }
}

而Java 8就可以這樣寫(xiě):

List<String> list = Arrays.asList("1one", "two", "three", "4four");
list.stream()// 1.得到容器的Steam
    .filter(str -> Character.isDigit(str.charAt(0)))// 2.選出以數(shù)字開(kāi)頭的字符串
    .forEach(str -> System.out.println(str));// 3.輸出字符串

上述代碼首先1. 調(diào)用List.stream()方法得到容器的Stream,2. 然后調(diào)用filter()方法過(guò)濾出以數(shù)字開(kāi)頭的字符串,3. 最后調(diào)用forEach()方法輸出結(jié)果。

使用Stream有兩個(gè)明顯的好處:

減少了模板代碼,只用Lambda表達(dá)式指明所需操作,代碼語(yǔ)義更加明確、便于閱讀。

將外部迭代改成了Stream的內(nèi)部迭代,方便了JVM本身對(duì)迭代過(guò)程做優(yōu)化(比如可以并行迭代)。

例子2

假設(shè)需要從一個(gè)字符串列表中,選出所有不以數(shù)字開(kāi)頭的字符串,將其轉(zhuǎn)換成大寫(xiě)形式,并把結(jié)果放到新的集合當(dāng)中。Java 8書(shū)寫(xiě)的代碼如下:

List<String> list = Arrays.asList("1one", "two", "three", "4four");
Set<String> newList =
        list.stream()// 1.得到容器的Stream
        .filter(str -> !Character.isDigit(str.charAt(0)))// 2.選出不以數(shù)字開(kāi)頭的字符串
        .map(String::toUpperCase)// 3.轉(zhuǎn)換成大寫(xiě)形式
        .collect(Collectors.toSet());// 4.生成結(jié)果集

上述代碼首先1. 調(diào)用List.stream()方法得到容器的Stream,2. 然后調(diào)用filter()方法選出不以數(shù)字開(kāi)頭的字符串,3. 之后調(diào)用map()方法將字符串轉(zhuǎn)換成大寫(xiě)形式,4. 最后調(diào)用collect()方法將結(jié)果轉(zhuǎn)換成Set。這個(gè)例子還向我們展示了方法引用(method references,代碼中標(biāo)號(hào)3處)以及收集器(Collector,代碼中標(biāo)號(hào)4處)的用法,這里不再展開(kāi)說(shuō)明。

通過(guò)這個(gè)例子我們看到了Stream鏈?zhǔn)讲僮?,即多個(gè)操作可以連成一串。不用擔(dān)心這會(huì)導(dǎo)致對(duì)容器的多次迭代,因?yàn)椴皇敲總€(gè)Stream的操作都會(huì)立即執(zhí)行。Stream的操作分成兩類(lèi),一類(lèi)是中間操作(intermediate operations),另一類(lèi)是結(jié)束操作(terminal operation),只有結(jié)束操作才會(huì)導(dǎo)致真正的代碼執(zhí)行,中間操作只會(huì)做一些標(biāo)記,表示需要對(duì)Stream進(jìn)行某種操作。這意味著可以在Stream上通過(guò)關(guān)聯(lián)多種操作,但最終只需要一次迭代。如果你熟悉Spark RDD,對(duì)此應(yīng)該并不陌生。

結(jié)語(yǔ)

Java 8引入Lambda表達(dá)式,從此打開(kāi)了函數(shù)式編程的大門(mén)。如果你之前不了解函數(shù)式編程,不必糾結(jié)于這個(gè)概念。編程過(guò)程中簡(jiǎn)潔明了的書(shū)寫(xiě)形式以及強(qiáng)大的Stream API會(huì)讓你很快熟悉Lambda表達(dá)式的。

本文只對(duì)Java Lambda表達(dá)式的基本介紹,希望能夠激發(fā)讀者對(duì)Java函數(shù)式編程的興趣。如果本文能夠讓你覺(jué)得Lambda表達(dá)式很好玩,函數(shù)式編程很有趣,并產(chǎn)生了進(jìn)一步學(xué)習(xí)的欲望,那就再好不過(guò)了。文末參考文獻(xiàn)中列出了一些有用的資源。


Release Notes

Popular Entries