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

資訊專欄INFORMATION COLUMN

【修煉內(nèi)功】[Java8] Lambda究竟是不是匿名類的語法糖

?xiaoxiao, / 2296人閱讀

摘要:本文已收錄修煉內(nèi)功躍遷之路初次接觸的時(shí)候感覺表達(dá)式很神奇表達(dá)式帶來的編程新思路,但又總感覺它就是匿名類或者內(nèi)部類的語法糖而已,只是語法上更為簡(jiǎn)潔罷了,如同以下的代碼匿名類內(nèi)部類編譯后會(huì)產(chǎn)生三個(gè)文件雖然從使用效果來看,與匿名類或者內(nèi)部類有相

本文已收錄【修煉內(nèi)功】躍遷之路

初次接觸Java8的時(shí)候感覺Lambda表達(dá)式很神奇(Lambda表達(dá)式帶來的編程新思路),但又總感覺它就是匿名類或者內(nèi)部類的語法糖而已,只是語法上更為簡(jiǎn)潔罷了,如同以下的代碼

public class Lambda {
    private static void hello(String name, Consumer printer) {
        printer.accept(name);
    }

    public static void main(String[] args) {
        hello("lambda", (name) -> System.out.println("Hello " + name));
        hello("匿名類", new Consumer () {
            @Override
            public void accept(String name) {
                System.out.println("Hello " + name);
            }
        });
        hello("內(nèi)部類", new SupplierImpl());
    }

    static class SupplierImpl implements Consumer {
        @Override
        public void accept(String name) {
            System.out.println("Hello " + name);
        }
    }
}

編譯后會(huì)產(chǎn)生三個(gè)文件

雖然從使用效果來看,Lambda與匿名類或者內(nèi)部類有相似之處(當(dāng)然也有很大不同,如this指針等 Lambda表達(dá)式里的"陷阱"),但從編譯結(jié)果來看,并不能簡(jiǎn)單地將Lambda與匿名類/內(nèi)部類劃等號(hào)

簡(jiǎn)單查看Lambda字節(jié)碼javap -p Lambda

Java編譯器自動(dòng)幫我們生成了方法lambda$main$0,我們有理由相信,Lambda表達(dá)式內(nèi)的邏輯就封裝在此函數(shù)內(nèi)

生成的方法lambda$main$0又是如何被調(diào)用的呢?

Lambda的調(diào)用使用了invokedynamic指令,虛擬機(jī)視角的方法調(diào)用一文中已經(jīng)詳細(xì)介紹了invokedynamic,但這里還是看不出invokedynamic指令與lambda$main$0方法之間到底是如何關(guān)聯(lián)起來的,invokedynamic指令的啟動(dòng)函數(shù)(BootstrapMethod)與調(diào)用點(diǎn)(CallSite)又在哪里?

其實(shí)仔細(xì)查看字節(jié)碼的話可以發(fā)下,編譯器還會(huì)額外生成一個(gè)內(nèi)部類

仔細(xì)查看內(nèi)部類的邏輯,是不是像極了虛擬機(jī)視角的方法調(diào)用一文中所提invokedynamic的運(yùn)行過程

在第一次執(zhí)行invokedynamic時(shí),JVM虛擬機(jī)會(huì)調(diào)用該指令所對(duì)應(yīng)的啟動(dòng)方法(BootstrapMethod)來生成調(diào)用點(diǎn)

啟動(dòng)方法(BootstrapMethod)由方法句柄來指定(MH_BootstrapMethod)

啟動(dòng)方法接受三個(gè)固定的參數(shù),分別為 Lookup實(shí)例、指代目標(biāo)方法名的字符串及該調(diào)用點(diǎn)能夠鏈接的方法句柄類型

將調(diào)用點(diǎn)綁定至該invokedynamic指令中,之后的運(yùn)行中虛擬機(jī)會(huì)直接調(diào)用綁定的調(diào)用點(diǎn)所鏈接的方法句柄

為了驗(yàn)證此想法,可以執(zhí)行java -Djdk.internal.lambda.dumpProxyClasses Lambda用來導(dǎo)出內(nèi)部類

跟蹤內(nèi)部類的運(yùn)行可以發(fā)現(xiàn),在執(zhí)行l(wèi)ambda表達(dá)式的時(shí)候會(huì)調(diào)用MethodHandleNatives.linkCallSite方法來生成并鏈接到調(diào)用點(diǎn)

// Up-calls from the JVM.
// These must NOT be public.

/**
  * The JVM is linking an invokedynamic instruction.  Create a reified call site for it.
  */
static MemberName linkCallSite(Object callerObj,
                               Object bootstrapMethodObj,
                               Object nameObj, Object typeObj,
                               Object staticArguments,
                               Object[] appendixResult) {
    MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
    Class caller = (Class)callerObj;
    String name = nameObj.toString().intern();
    MethodType type = (MethodType)typeObj;
    if (!TRACE_METHOD_LINKAGE)
        return linkCallSiteImpl(caller, bootstrapMethod, name, type,
                                staticArguments, appendixResult);
    return linkCallSiteTracing(caller, bootstrapMethod, name, type,
                               staticArguments, appendixResult);
}
static MemberName linkCallSiteImpl(Class caller,
                                   MethodHandle bootstrapMethod,
                                   String name, MethodType type,
                                   Object staticArguments,
                                   Object[] appendixResult) {
    CallSite callSite = CallSite.makeSite(bootstrapMethod,
                                          name,
                                          type,
                                          staticArguments,
                                          caller);
    if (callSite instanceof ConstantCallSite) {
        appendixResult[0] = callSite.dynamicInvoker();
        return Invokers.linkToTargetMethod(type);
    } else {
        appendixResult[0] = callSite;
        return Invokers.linkToCallSiteMethod(type);
    }
}

caller調(diào)用lambda方法的類Lambda [Class]

bootstrapMethod為啟動(dòng)方法的句柄 [MethodHandler]

name為lambda表達(dá)式實(shí)際類型中需要執(zhí)行的方法名acccept (Consumer.accept)

type為生成的方法類型()Consumer [MethodType]

staticArguments中包含了lambda方法的方法句柄 [MethodHandler] 及方法類型 [MethodType]

CallSite.makeSite方法會(huì)生成調(diào)用點(diǎn),最終調(diào)用如class文件中所示的LambdaMetafactory.metafactory方法

public static CallSite metafactory(MethodHandles.Lookup caller,
                                   String invokedName,
                                   MethodType invokedType,
                                   MethodType samMethodType,
                                   MethodHandle implMethod,
                                   MethodType instantiatedMethodType)
    throws LambdaConversionException {
    AbstractValidatingLambdaMetafactory mf;
    mf = new InnerClassLambdaMetafactory(caller, invokedType,
                                         invokedName, samMethodType,
                                         implMethod, instantiatedMethodType,
                                         false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
    mf.validateMetafactoryArgs();
    return mf.buildCallSite();
}

簡(jiǎn)單來講,所生成內(nèi)部類作用,是為了生成調(diào)用點(diǎn),并鏈接到invokedynamic指令,以便動(dòng)態(tài)調(diào)用

如果一個(gè)類中,多次使用lambda表達(dá)式,會(huì)生成多少個(gè)方法,又會(huì)生成多少個(gè)內(nèi)部類?

public class Lambda {
    private static void hello(String name, Consumer printer) {
        printer.accept(name);
    }

    public static void main(String[] args) {
        hello("lambda1", (name) -> System.out.println("Hello " + name));
        hello("lambda2", (name) -> System.out.println("Hello " + name));
        new Thread(() -> {
            System.out.println("thread");
        }).start();
    }
}

上述示例中共使用了三處、兩種(Consumer、Runnable)Lambda表達(dá)式,編譯后查看class文件

對(duì)于每一個(gè)lambda表達(dá)式,都會(huì)生成一個(gè)靜態(tài)的私有方法

再查看內(nèi)部類

只會(huì)生成一個(gè)內(nèi)部類,但存在三個(gè)方法,每個(gè)方法對(duì)應(yīng)一個(gè)lambda私有方法,用以生成對(duì)應(yīng)的調(diào)用點(diǎn)綁定到相應(yīng)的invokedynamic指令上

結(jié)合以上我們可以總結(jié)出:

lambda表達(dá)式會(huì)被編譯為invokedynamic指令

每一個(gè)lambda表達(dá)式的實(shí)現(xiàn)邏輯均會(huì)被封裝為一個(gè)靜態(tài)私有方法

只要存在lambda表達(dá)式調(diào)用,便會(huì)生成一個(gè)內(nèi)部類

內(nèi)部類中每一個(gè)方法(啟動(dòng)方法 BoostrapMethod)對(duì)應(yīng)一個(gè)lambda表達(dá)式所生成的靜態(tài)私有方法,內(nèi)部類中的方法用以生成對(duì)應(yīng)的調(diào)用點(diǎn)綁定到相應(yīng)的invokedynamic指令上

這也解釋了為什么lambda中的this指針指向的是周圍的類 (定義該Lambda表達(dá)式時(shí)所處的類) (Lambda表達(dá)式里的"陷阱")

所以,lambda表達(dá)式確實(shí)是語法糖,但并不是匿名類/內(nèi)部類的語法糖

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

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

相關(guān)文章

  • 修煉內(nèi)功】[JVM] 虛擬機(jī)視角的方法調(diào)用

    摘要:本文已收錄修煉內(nèi)功躍遷之路我們寫的方法在被編譯為文件后是如何被虛擬機(jī)執(zhí)行的對(duì)于重寫或者重載的方法,是在編譯階段就確定具體方法的么如果不是,虛擬機(jī)在運(yùn)行時(shí)又是如何確定具體方法的方法調(diào)用不等于方法執(zhí)行,一切方法調(diào)用在文件中都只是常量池中的符號(hào)引 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbuesq?w=2114&h=12...

    shevy 評(píng)論0 收藏0
  • Java8流特性和Lambda表達(dá)式

    摘要:表達(dá)式體現(xiàn)了函數(shù)式編程的思想,即一個(gè)函數(shù)亦可以作為另一個(gè)函數(shù)參數(shù)和返回值,使用了函數(shù)作參數(shù)返回值的函數(shù)被稱為高階函數(shù)。對(duì)流對(duì)象進(jìn)行及早求值,返回值不在是一個(gè)對(duì)象。 Java8主要的改變是為集合框架增加了流的概念,提高了集合的抽象層次。相比于舊有框架直接操作數(shù)據(jù)的內(nèi)部處理方式,流+高階函數(shù)的外部處理方式對(duì)數(shù)據(jù)封裝更好。同時(shí)流的概念使得對(duì)并發(fā)編程支持更強(qiáng)。 在語法上Java8提供了Lamb...

    gaara 評(píng)論0 收藏0
  • 轉(zhuǎn) | Java8初體驗(yàn)(一)lambda表達(dá)式語法

    摘要:初體驗(yàn)下面進(jìn)入本文的正題表達(dá)式。接下來展示表達(dá)式和其好基友的配合。吐槽一下方法引用表面上看起來方法引用和構(gòu)造器引用進(jìn)一步簡(jiǎn)化了表達(dá)式的書寫,但是個(gè)人覺得這方面沒有的下劃線語法更加通用。 感謝同事【天錦】的投稿。投稿請(qǐng)聯(lián)系 [email protected] 本文主要記錄自己學(xué)習(xí)Java8的歷程,方便大家一起探討和自己的備忘。因?yàn)楸救艘彩莿倓傞_始學(xué)習(xí)Java8,所以文中肯定有錯(cuò)誤和理解偏...

    Lucky_Boy 評(píng)論0 收藏0
  • Java8 Lambda本質(zhì)論

    摘要:的本質(zhì)需求按照產(chǎn)品的重量進(jìn)行升序排序此處使用匿名內(nèi)部類的設(shè)計(jì),但摻雜了較多的語法噪聲,引入了不必要的復(fù)雜度。使用表達(dá)式,可以進(jìn)一步消除語法噪聲,簡(jiǎn)化設(shè)計(jì)。方法引用其本質(zhì)是具有單一方法調(diào)用的表達(dá)式的語法糖表示。 Lambda的本質(zhì) 需求1. 按照產(chǎn)品的重量進(jìn)行升序排序 此處使用「匿名內(nèi)部類」的設(shè)計(jì),但摻雜了較多的語法噪聲,引入了不必要的復(fù)雜度。 Collections.sort(repo...

    twohappy 評(píng)論0 收藏0
  • Ummm... Java8lambda

    摘要:引入了與此前完全不同的函數(shù)式編程方法,通過表達(dá)式和來為下的函數(shù)式編程提供動(dòng)力。命令式編程語言把對(duì)象變量和流轉(zhuǎn)當(dāng)作一等公民,而函數(shù)式編程在此基礎(chǔ)上加入了策略變量這一新的一等公民。 Java8引入了與此前完全不同的函數(shù)式編程方法,通過Lambda表達(dá)式和StreamAPI來為Java下的函數(shù)式編程提供動(dòng)力。本文是Java8新特性的第一篇,旨在闡釋函數(shù)式編程的本義,更在展示Java是如何通...

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

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

0條評(píng)論

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