成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java8新特性第1章(Lambda表達(dá)式)

ningwang / 863人閱讀

摘要:一表達(dá)式匿名內(nèi)部類最大的問(wèn)題在于其冗余的語(yǔ)法,比如前面的中五行代碼僅有一行是在執(zhí)行任務(wù)??偨Y(jié)基于詞法作用域的理念,表達(dá)式不可以掩蓋任何其所在上下文的局部變量。

轉(zhuǎn)載請(qǐng)注明出處:https://zhuanlan.zhihu.com/p/20540175

在介紹Lambda表達(dá)式之前,我們先來(lái)看只有單個(gè)方法的Interface(通常我們稱之為回調(diào)接口):

public interface OnClickListener {
    void onClick(View v);
}

我們是這樣使用它的:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        v.setText("lalala");
       }
});

這種回調(diào)模式在各種框架中非常流行,但是像上面這樣的匿名內(nèi)部類并不是一個(gè)好的選擇,因?yàn)椋?/p>

語(yǔ)法冗余;

匿名內(nèi)部類中的this指針和變量容易產(chǎn)生誤解;

無(wú)法捕獲非final局部變量;

非靜態(tài)內(nèi)部類默認(rèn)持有外部類的引用,部分情況下會(huì)導(dǎo)致外部類無(wú)法被GC回收,導(dǎo)致內(nèi)存泄露。

令人高興的是Java8為我們帶來(lái)了Lambda,下面我們看看利用Lambda如何實(shí)現(xiàn)上面的功能:

button.setOnClickListener(v -> v.setText("lalala"));

怎么樣?!五行代碼用一行就搞定了!?。?/p>

在這里補(bǔ)充個(gè)概念函數(shù)式接口;前面提到的OnClickListener接口只有一個(gè)方法,Java中大多數(shù)回調(diào)接口都有這個(gè)特征:比如Runnable和Comparator;我們把這些只擁有一個(gè)方法的接口稱之為函數(shù)式接口。

一、Lambda表達(dá)式

匿名內(nèi)部類最大的問(wèn)題在于其冗余的語(yǔ)法,比如前面的OnClickListener中五行代碼僅有一行是在執(zhí)行任務(wù)。Lambda表達(dá)式是匿名方法,前面我們也看到了它用極其輕量的語(yǔ)法解決了這一問(wèn)題。

下面給大家看幾個(gè)Lambda表達(dá)式的例子:

(int x, int y) -> x + y                      //接收x和y兩個(gè)整形參數(shù)并返回他們的和
() -> 66                                     //不接收任何參數(shù)直接返回66
(String name) -> {System.out.println(name);} //接收一個(gè)字符串然后打印出來(lái)
(View view) -> {view.setText("lalala");}     //接收一個(gè)View對(duì)象并調(diào)用setText方法

Lambda表達(dá)式語(yǔ)法由參數(shù)列表->函數(shù)體組成。函數(shù)體既可以是一個(gè)表達(dá)式也可以是一個(gè)代碼塊。

__表達(dá)式__:表達(dá)式會(huì)被執(zhí)行然后返回結(jié)果。它簡(jiǎn)化掉了return關(guān)鍵字。

__代碼塊__:顧名思義就是一坨代碼,和普通方法中的語(yǔ)句一樣。

二、目標(biāo)類型

通過(guò)前面的例子我們可以看到,lambda表達(dá)式?jīng)]有名字,那我們?cè)趺粗浪念愋湍??答案是通過(guò)上下文推導(dǎo)而來(lái)的。例如,下面的表達(dá)式的類型是OnClickListener

OnClickListener listener = (View v) -> {v.setText("lalala");};

這就意味著同樣的lambda表達(dá)式在不同的上下文里有不同的類型

Runnable runnable = () -> doSomething();  //這個(gè)表達(dá)式是Runnable類型的
Callback callback = () -> doSomething();  //這個(gè)表達(dá)式是Callback類型的

編譯器利用lambda表達(dá)式所在的上下文所期待的類型來(lái)推導(dǎo)表達(dá)式的類型,這個(gè)__被期待的類型__被稱為目標(biāo)類型。lambda表達(dá)式只能出現(xiàn)在__目標(biāo)類型__為函數(shù)式接口的上下文中。

Lambda表達(dá)式的類型和目標(biāo)類型的方法簽名必須一致,編譯器會(huì)對(duì)此做檢查,一個(gè)lambda表達(dá)式要想賦值給目標(biāo)類型T則必須滿足下面所有的條件:

T是一個(gè)函數(shù)式接口

lambda表達(dá)式的參數(shù)必須和T的方法參數(shù)在數(shù)量、類型和順序上一致(一一對(duì)應(yīng))

lambda表達(dá)式的返回值必須和T的方法的返回值一致或者是它的子類

lambda表達(dá)式拋出的異常和T的方法的異常一致或者是它的子類

由于目標(biāo)類型是知道lambda表達(dá)式的參數(shù)類型,所以我們沒(méi)必要把已知的類型重復(fù)一遍。也就是說(shuō)lambda表達(dá)式的參數(shù)類型可以從目標(biāo)類型獲?。?/p>

//編譯器可以推導(dǎo)出s1和s2是String類型
Comparator c = (s1, s2) -> s1.compareTo(s2);
//當(dāng)表達(dá)式的參數(shù)只有一個(gè)時(shí)括號(hào)也是可以省略的
button.setOnClickListener(v -> v.setText("lalala"));

ps: Java7中的泛型方法和<>構(gòu)造器也是通過(guò)目標(biāo)類型來(lái)進(jìn)行類型推導(dǎo)的,如:

List intList = Collections.emptyList>();
List strList = new ArrayList<>();
三、作用域

在內(nèi)部類中使用變量名和this非常容易出錯(cuò)。內(nèi)部類通過(guò)繼承得到的成員變量(包括來(lái)說(shuō)object的)可能會(huì)把外部類的成員變量覆蓋掉,未做限制的this引用會(huì)指向內(nèi)部類自己而非外部類。

而lambda表達(dá)式的語(yǔ)義就十分簡(jiǎn)單:它不會(huì)從父類中繼承任何變量,也不用引入新的作用域。lambda表達(dá)式的參數(shù)及函數(shù)體里面的變量和它外部環(huán)境的變量具有相同的語(yǔ)義(this關(guān)鍵字也是一樣)。

下面我們舉個(gè)栗子吧!

public class HelloLambda {

    Runnable r1 = () -> System.out.println(this);
    Runnable r2 = () -> System.out.println(toString());

    @Override
    public String toString() {
        return "Hello, lambda!";
    }

    public static void main(String[] args) {
        new HelloLambda().r1.run();  
        new HelloLambda().r2.run();
    }
}

上面的代碼最終會(huì)打印兩個(gè)Hello, lambda!,與之相類似的內(nèi)部類則會(huì)打印出類似HelloLambda$1@32a890HelloLambda$1@6b32098這種出乎意料的字符串。

總結(jié):基于詞法作用域的理念,lambda表達(dá)式不可以掩蓋任何其所在上下文的局部變量。

四、變量捕獲

在Java7中,編譯器對(duì)內(nèi)部類中引用的外部變量(即捕獲的變量)要求非常嚴(yán)格:如果捕獲的變量沒(méi)有被聲明為final就會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤。但是在Java8中放寬了這一限制--對(duì)于lambda表達(dá)式和內(nèi)部類,允許在其中捕獲那些符合有效只讀的局部變量(如果一個(gè)局部變量在初始化后從未被修改過(guò),那么它就是有效只讀)。

Runnable getRunnable(String name){
    String hello = "hello";
    return () -> System.out.println(hello+","+name);
}

對(duì)于this的引用以及通過(guò)this對(duì)未限定字段的引用和未限定方法的調(diào)用本質(zhì)上都屬于使用final局部變量。包含此類引用的lambda表達(dá)式相當(dāng)于捕獲了this實(shí)例。在其他情況下,lambda對(duì)象不會(huì)保留任何對(duì)this的應(yīng)用。

這個(gè)特性對(duì)內(nèi)存管理是極好的:要知道在java中一個(gè)非靜態(tài)內(nèi)部類會(huì)默認(rèn)持有外部類實(shí)例的強(qiáng)引用,這往往會(huì)造成內(nèi)存泄露。而在lambda表達(dá)式中如果沒(méi)有捕獲外部類成員則不會(huì)保留對(duì)外部類實(shí)例的引用。

不過(guò)盡管Java8放寬了對(duì)捕獲變量的語(yǔ)法限制,但試圖修改捕獲變量的行為是被禁止的,比如下面這個(gè)例子就是非法的:

int sum  = 0;
list.forEach(i -> {sum += i;});

為什么要禁止這種行為呢?因?yàn)檫@樣的lambda表達(dá)式很容易引起race condition

lambda表達(dá)式不支持修改捕獲變量的另外一個(gè)原因是我們可以使用更好的方式來(lái)實(shí)現(xiàn)同樣的效果:使用規(guī)約(condition)。java.util.stream包提供了各種規(guī)約操作,關(guān)于Java8中的Stream API我們放到下一章介紹。

五、方法引用

lambda表達(dá)式允許我們定義一個(gè)匿名方法,并以函數(shù)式接口的方式使用它。Java8能夠在已有的方法上實(shí)現(xiàn)同樣的特性。

方法引用和lambda表達(dá)式擁有相同的特性(他們都需要一個(gè)目標(biāo)類型,并且需要被轉(zhuǎn)化為函數(shù)式接口的實(shí)例),不過(guò)我們不需要為方法引用提供方法體,我們可以直接通過(guò)方法名引用已有方法。

以下面的代碼為例,假設(shè)我們要按照userName排序

class User{

    private String userName;

    public String getUserName() {
        return userName;
    }
    ...
}

List users = new ArrayList<>();
Comparator comparator = Comparator.comparing(u -> u.getUserName());
Collections.sort(users, comparator);

我們可以用方法引用替換上面的lambda表達(dá)式

Comparator comparator = Comparator.comparing(User::getUserName);

這里的User::getUserName被看做是lambda表達(dá)式的簡(jiǎn)寫形式。盡管方法引用不一定會(huì)把代碼變得更緊湊,但它擁有更明確的語(yǔ)義--如果我們想要調(diào)用的方法擁有一個(gè)名字,那么我們就可以通過(guò)方法名調(diào)用它。

方法引用有很多種,它們的語(yǔ)法如下:

靜態(tài)方法引用:ClassName::methodName

實(shí)例上的實(shí)例方法引用:instanceReference::methodName

超類上的實(shí)例方法引用:super::methodName

類型上的實(shí)例方法引用:ClassName::methodName

構(gòu)造方法引用:Class::new

數(shù)組構(gòu)造方法引用:TypeName[]::new

如果大家喜歡這一系列的文章,歡迎關(guān)注我的知乎專欄、GitHub、簡(jiǎn)書博客。

知乎專欄:https://zhuanlan.zhihu.com/baron

GitHub:https://github.com/BaronZ88

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66491.html

相關(guān)文章

  • 在Android項(xiàng)目中使用Java8

    摘要:現(xiàn)在爸爸終于讓平臺(tái)支持了,這篇文章中便來(lái)和大家聊聊如何在項(xiàng)目中配置使用。要想在項(xiàng)目中使用的新特性,需要將你的升級(jí)到及以上版本,并采用新的編譯。 轉(zhuǎn)載請(qǐng)注明出處:https://zhuanlan.zhihu.com/p/23279894 前言 在過(guò)去的文章中我介紹過(guò)Java8的一些新特性,包括: Java8新特性第1章(Lambda表達(dá)式) Java8新特性第2章(接口默認(rèn)方法) J...

    junnplus 評(píng)論0 收藏0
  • Java8特性3(Stream API)

    摘要:另外,像這樣生成值的操作和這樣產(chǎn)生副作用的操作都是天然急性求值,因?yàn)樗鼈儽仨氁a(chǎn)生具體的結(jié)果。這樣可以大大減少維持中間結(jié)果所帶來(lái)的開銷?,F(xiàn)在我們需要篩選出含有平米以上房源的小區(qū),并按照小區(qū)名排序。 轉(zhuǎn)載請(qǐng)注明出處:https://zhuanlan.zhihu.com/p/20540202 Stream作為Java8的新特性之一,他與Java IO包中的InputStream和Outp...

    andycall 評(píng)論0 收藏0
  • Java學(xué)習(xí)路線總結(jié),搬磚工逆襲Java架構(gòu)師(全網(wǎng)最強(qiáng))

    摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡(jiǎn)介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁(yè)左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無(wú)意間聽(tīng)到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡(jiǎn)而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...

    Scorpion 評(píng)論0 收藏0
  • Java8實(shí)戰(zhàn)》-讀書筆記(01

    摘要:依舊使用剛剛對(duì)蘋果排序的代碼。現(xiàn)在,要做的是篩選出所有的綠蘋果,也許你會(huì)這一個(gè)這樣的方法在之前,基本上都是這樣寫的,看起來(lái)也沒(méi)什么毛病。但是,現(xiàn)在又要篩選一下重量超過(guò)克的蘋果。 《Java8實(shí)戰(zhàn)》-讀書筆記第一章(01) 最近一直想寫點(diǎn)什么東西,卻不知該怎么寫,所以就寫寫關(guān)于看《Java8實(shí)戰(zhàn)》的筆記吧。 第一章內(nèi)容較多,因此打算分幾篇文章來(lái)寫。 為什么要關(guān)心Java8 自1996年J...

    codeGoogle 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<