溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Scala和Java8怎么寫Lambda表達(dá)式

發(fā)布時(shí)間:2021-12-08 16:59:18 來源:億速云 閱讀:96 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“Scala和Java8怎么寫Lambda表達(dá)式”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Scala和Java8怎么寫Lambda表達(dá)式”吧!

Java的寫法 –

1List names = Arrays.asList("1", "2", "3");

2Stream lengths = names.stream().map(name -> name.length());

Scala的寫法 –

1.val names = List("1", "2", "3")

2.val lengths = names.map(name =>name.length)

表面上看起來非常簡(jiǎn)單,那么后面的復(fù)雜東西是怎么搞的呢?

一起分析Scala的實(shí)現(xiàn)方式

The Code

我使用javap(jdk自帶的工具)去查看Scala編譯器編譯出來的class類中所包含的字節(jié)碼內(nèi)容。讓我們一起看看最終的字節(jié)碼(這是JVM將真正執(zhí)行的)

1.// 加載names對(duì)象引用,壓入操作棧(JVM把它當(dāng)成變量#2)

2.// 它將停留一會(huì),直到被map函數(shù)調(diào)用.

3.aload_2

接下來的東西變得更加有趣了,編譯器產(chǎn)生的一個(gè)合成類的實(shí)例被創(chuàng)建和初始化。從JVM角度,就是通過這個(gè)對(duì)象持有Lambda方法的。有趣的是雖然Lambda被定義為我們方法的一個(gè)組成部分,但實(shí)際上它完全存在于我們的類之外。

new myLambdas/Lambda1$$anonfun$1 //new一個(gè)lambda實(shí)例變量.
dup //把lambda實(shí)例變量引用壓入操作棧.

// 最后,調(diào)用它的構(gòu)造方法.記住,對(duì)于JVM來說,它僅僅只是一個(gè)普通對(duì)象.
invokespecial myLambdas/Lambda1$$anonfun$1/()V

//這兩行長的代碼加載了用于創(chuàng)建list的immutable.List CanBuildFrom工廠。
//這個(gè)工廠模式是Scala集合架構(gòu)的一部分。
getstatic scala/collection/immutable/List$/MODULE$
Lscala/collection/immutable/List$;
invokevirtual scala/collection/immutable/List$/canBuildFrom()
Lscala/collection/generic/CanBuildFrom;

// 現(xiàn)在我們的操作棧中已經(jīng)有了Lambda對(duì)象和工廠
// 接下來的步驟是調(diào)用map函數(shù)。
// 如果你記得,我們一開始已經(jīng)將names對(duì)象引用壓入操作棧頂。
// names對(duì)象現(xiàn)在被作為map方法調(diào)用的實(shí)例,
// 它也可以接受Lambda對(duì)象和工廠用于生成一個(gè)包含字符串長度的新集合。
invokevirtual scala/collection/immutable/List/map(Lscala/Function1;
Lscala/collection/generic/CanBuildFrom;)Ljava/lang/Object;

但是,等等,Lambda對(duì)象內(nèi)部到底發(fā)生了什么呢?

Lambda 對(duì)象

Lambda類衍生自scala.runtime.AbstractFunction1。通過調(diào)用map函數(shù)可以多態(tài)調(diào)用被重寫的apply方法,被重寫的apply方法代碼如下:

aload_0 //加載this對(duì)象引用到操作棧
aload_1 //加載字符串參數(shù)到操作棧
checkcast java/lang/String //檢查是不是字符串類型

// 調(diào)用合成類中重寫的apply方法
invokevirtual myLambdas/Lambda1$$anonfun$1/apply(Ljava/lang/String;)I

//包裝返回值
invokestatic scala/runtime/BoxesRunTime/boxToInteger(I)Ljava/lang/Integer
areturn

真正用于執(zhí)行l(wèi)ength()操作的代碼被嵌套在額外的apply方法中,用于簡(jiǎn)單的返回我們所期望的字符串長度。

我們前面走了一段很長的路,終于到這邊了:

aload_1

invokevirtual java/lang/String/length()I

ireturn

對(duì)于我們上面寫的簡(jiǎn)單的代碼,最后生成了大量的字節(jié)碼,一個(gè)額外的類和一堆新的方法。當(dāng)然,這并不意味著會(huì)讓我們放棄使用Lambda(我們是在寫scala,不是C)。這僅僅表明了這些結(jié)構(gòu)后面的復(fù)雜性.試想Lambda表達(dá)式的代碼和復(fù)雜的東西將被編譯成復(fù)雜的執(zhí)行鏈。

我預(yù)計(jì)Java8會(huì)以相同的方式實(shí)現(xiàn)Lambda,但出人意料的是,他們使用了另一種完全不同的方式。

Java 8 – 新的實(shí)現(xiàn)方式

Java8的實(shí)現(xiàn),字節(jié)碼比較短,但是做的事情卻很意外。它一開始很簡(jiǎn)單地加載names變量,并且調(diào)用它的stream方法,但它接下來做的東東就顯得很優(yōu)雅了.它使用一個(gè)Java7加入的一個(gè)新指令invokeDynamic去動(dòng)態(tài)地連接lambda函數(shù)的真正調(diào)用點(diǎn),從而代替創(chuàng)建一個(gè)用于包裝lambda函數(shù)的對(duì)象.

aload_1 //加載names對(duì)象引用,壓入操作棧

//調(diào)用它的stream()方法
invokeinterface java/util/List.stream:()Ljava/util/stream/Stream;

//神奇的invokeDynamic指令!
invokedynamic #0:apply:()Ljava/util/function/Function;

//調(diào)用map方法
invokeinterface java/util/stream/Stream.map:
(Ljava/util/function/Function;)Ljava/util/stream/Stream;

神奇的InvokeDynamic指令. 這個(gè)是JAVA 7新加入的指令,它使得JVM限制少了,并且允許動(dòng)態(tài)語言運(yùn)行時(shí)綁定符號(hào).

動(dòng)態(tài)鏈接. 如果你看到invokedynamic指令,你會(huì)發(fā)現(xiàn)實(shí)際上沒有任何Lambda函數(shù)的引用(名為lambda$0),這是因?yàn)閕nvokedynamic的設(shè)計(jì)方式,簡(jiǎn)單地說就是lambda的名稱和簽名,如我們的例子-

// 一個(gè)名為Lamda$0的方法,獲得一個(gè)字符串參數(shù)并返回一個(gè)Integer對(duì)象

lambdas/Lambda1.lambda$0:(Ljava/lang/String;)Ljava/lang/Integer;

他們保存在.class文件中一個(gè)單獨(dú)的表的條目中,執(zhí)行invokedynamic時(shí)會(huì)將#0參數(shù)傳給指令指針。這個(gè)新的表的確在很多年后的今天首次改變了字節(jié)碼規(guī)范的結(jié)構(gòu),這也就需要我們改編Takipi的錯(cuò)誤分析引擎來配合。

The Lambda code

下面這個(gè)字節(jié)碼是真正的lambda表達(dá)式.然后就是千篇一律地、簡(jiǎn)單地加載字符串參數(shù),調(diào)用length方法獲得長度,并且包裝返回值.注意它是作為靜態(tài)方法編譯的,從而避免了傳遞一個(gè)額外的this對(duì)象給他,就像我們前面看到的Scala中的做法.

aload_0

invokevirtual java/lang/String.length:()

invokestatic java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

areturn

invokedynamic 方式的另一個(gè)優(yōu)點(diǎn)是,它允許我們使用map函數(shù)多態(tài)地調(diào)用這個(gè)方法,而不需要去實(shí)例化一個(gè)封裝對(duì)象或調(diào)用重寫的方法.非??岚?

到此,相信大家對(duì)“Scala和Java8怎么寫Lambda表達(dá)式”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI