摘要:編程語(yǔ)言將函數(shù)作為一等公民,函數(shù)可以被作為參數(shù)或者返回值傳遞,因?yàn)樗灰暈閷?duì)象。是表示已注釋接口是函數(shù)接口的注釋。如果一個(gè)函數(shù)有一個(gè)或多個(gè)參數(shù)并且有返回值呢為了解決這個(gè)問(wèn)題,提供了一系列通用函數(shù)接口,在包里。
【編者按】雖然 Java 深得大量開(kāi)發(fā)者喜愛(ài),但是對(duì)比其他現(xiàn)代編程語(yǔ)言,其語(yǔ)法確實(shí)略顯冗長(zhǎng)。但是通過(guò) Java8,直接利用 lambda 表達(dá)式就能編寫(xiě)出既可讀又簡(jiǎn)潔的代碼。作者 Hussachai Puripunpinyo 的軟件工程師,作者通過(guò)對(duì)比 Java 8和 Scala,對(duì)性能和表達(dá)方面的差異進(jìn)行了分析,并且深入討論關(guān)于 Stream API 的區(qū)別,本文由 OneAPM 工程師編譯整理。
數(shù)年等待,Java 8 終于添加了高階函數(shù)這個(gè)特性。本人很喜歡 Java,但不得不承認(rèn),相比其他現(xiàn)代編程語(yǔ)言,Java 語(yǔ)法非常冗長(zhǎng)。然而通過(guò) Java8,直接利用 lambda 表達(dá)式就能編寫(xiě)出既可讀又簡(jiǎn)潔的代碼(有時(shí)甚至比傳統(tǒng)方法更具可讀性)。
Java 8于2014年3月3日發(fā)布,但筆者最近才有機(jī)會(huì)接觸。因?yàn)楣P者也很熟悉 Scala,所以就產(chǎn)生了對(duì)比 Java 8和Scala 在表達(dá)性和性能方面的差異,比較將圍繞 Stream API 展開(kāi),同時(shí)也會(huì)介紹如何使用 Stream API 來(lái)操作集合。
由于文章太長(zhǎng),所以分以下三個(gè)部分詳細(xì)敘述。
Part 1.Lambda 表達(dá)式
Part 2. Stream API vs Scala collection API
Part 3. Trust no one, bench everything(引用自sbt-jmh)
首先,我們來(lái)了解下 Java 8的 lambda 表達(dá)式,雖然不知道即使表達(dá)式部分是可替代的,他們卻稱(chēng)之為 lambda 表達(dá)式。這里完全可以用聲明來(lái)代替表達(dá)式,然后說(shuō) Java 8還支持 lambda 聲明。編程語(yǔ)言將函數(shù)作為一等公民,函數(shù)可以被作為參數(shù)或者返回值傳遞,因?yàn)樗灰暈閷?duì)象。Java是一種靜態(tài)的強(qiáng)類(lèi)型語(yǔ)言。所以,函數(shù)必須有類(lèi)型,因此它也是一個(gè)接口。
另一方面,lambda 函數(shù)就是實(shí)現(xiàn)了函數(shù)接口的一個(gè)類(lèi)。無(wú)需創(chuàng)建這個(gè)函數(shù)的類(lèi),編譯器會(huì)直接實(shí)現(xiàn)。不幸的是,Java 沒(méi)有 Scala 那樣高級(jí)的類(lèi)型接口。如果你想聲明一個(gè) lambda 表達(dá)式,就必須指定目標(biāo)類(lèi)型。實(shí)際上,由于 Java 必須保持向后兼容性,這也是可理解的,而且就目前來(lái)說(shuō) Java 完成得很好。例如,Thread.stop() 在 JDK 1.0版時(shí)發(fā)布,已過(guò)時(shí)了十多年,但即便到今天仍然還在使用。所以,不要因?yàn)檎Z(yǔ)言 XYZ 的語(yǔ)法(或方法)更好,就指望 Java 從根本上改變語(yǔ)法結(jié)構(gòu)。
所以,Java 8的語(yǔ)言設(shè)計(jì)師們奇思妙想,做成函數(shù)接口!函數(shù)接口是只有一個(gè)抽象方法的接口。要知道,大多數(shù)回調(diào)接口已經(jīng)滿(mǎn)足這一要求。因此,我們可以不做任何修改重用這些接口。@FunctionalInterface 是表示已注釋接口是函數(shù)接口的注釋。此注釋是可選的,除非有檢查要求,否則不用再進(jìn)行處理。
請(qǐng)記住,lambda 表達(dá)式必須定義類(lèi)型,而該類(lèi)型必須只有一個(gè)抽象方法。
//Before Java 8 Runnable r = new Runnable(){ public void run(){ System.out.println(“This should be run in another thread”); } }; //Java 8 Runnable r = () -> System.out.println(“This should be run in another thread”);
如果一個(gè)函數(shù)有一個(gè)或多個(gè)參數(shù)并且有返回值呢?為了解決這個(gè)問(wèn)題,Java 8提供了一系列通用函數(shù)接口,在java.util.function包里。
//Java 8 FunctionparseInt = (String s) -> Integer.parseInt(s);
該參數(shù)類(lèi)型可以從函數(shù)中推斷,就像 Java7中的diamond operator,所以可以省略。我們可以重寫(xiě)該函數(shù),如下所示:
//Java 8 FunctionparseInt = s -> Integer.parseInt(s);
如果一個(gè)函數(shù)有兩個(gè)參數(shù)呢?無(wú)需擔(dān)心,Java 8 中有 BiFunction。
//Java 8 BiFunctionmultiplier = (i1, i2) -> i1 * i2; //you can’t omit parenthesis here!
如果一個(gè)函數(shù)接口有三個(gè)參數(shù)呢?TriFunction?語(yǔ)言設(shè)計(jì)者止步于 BiFunction。否則,可能真會(huì)有 TriFunction、quadfunction、pentfunction 等。解釋一下,筆者是采用 IUPAC 規(guī)則來(lái)命名函數(shù)的。然后,可以按如下所示定義 TriFunction。
//Java 8 @FunctionalInterface interface TriFunction { public R apply(A a, B b, C c); }
然后導(dǎo)入接口,并把它當(dāng)作 lambda 表達(dá)式類(lèi)型使用。
//Java 8 TriFunctionsumOfThree = (i1, i2, i3) -> i1 + i2 + i3;
這里你應(yīng)該能理解為什么設(shè)計(jì)者止步于 BiFunction。
如果還沒(méi)明白,不妨看看 PentFunction,假設(shè)我們?cè)谄渌胤揭呀?jīng)定義了 PentFunction。
//Java 8 PentFunctionsumOfFive = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5;
你知道 Ennfunction 是多長(zhǎng)嗎?(拉丁語(yǔ)中,enn 表示9)你必須申報(bào) 10 種類(lèi)型(前 9 個(gè)是參數(shù),最后一個(gè)是返回類(lèi)型),大概整行都只有類(lèi)型了。那么聲明一個(gè)類(lèi)型是否有必要呢?答案是肯定的。(這也是為什么筆者認(rèn)為 Scala 的類(lèi)型接口比 Java 的更好)
Scala 也有其 lambda 表達(dá)式類(lèi)型。在 Scala 中,你可以創(chuàng)建有22個(gè)參數(shù)的 lambda 表達(dá)式,意味著 Scala 有每個(gè)函數(shù)的類(lèi)型(Function0、Function1、……Function22)。函數(shù)類(lèi)型在 Scala 函數(shù)中是一個(gè) Trait,Trait 就像 Java 中的抽象類(lèi),但可以當(dāng)做混合類(lèi)型使用。如果還需要22個(gè)以上的參數(shù),那大概是你函數(shù)的設(shè)計(jì)有問(wèn)題。必須要考慮所傳遞的一組參數(shù)的類(lèi)型。在此,筆者將不再贅述關(guān)于 Lambda 表達(dá)式的細(xì)節(jié)。
下面來(lái)看看Scala的其他內(nèi)容。Scala 也是類(lèi)似 Java 的靜態(tài)強(qiáng)類(lèi)型語(yǔ)言,但它一開(kāi)始就是函數(shù)語(yǔ)言。因此,它能很好地融合面向?qū)ο蠛秃瘮?shù)編程。由于 Scala 和 Java 所采用的方法不同,這里不能給出 Runnable 的 Scala 實(shí)例。Scala 有自己解決問(wèn)題的方法,所以接下來(lái)會(huì)詳細(xì)探討。
//Scala
Future(println{“This should be run in another thread”})
與以下 Java8 的代碼等效。
//Java 8
//assume that you have instantiated ExecutorService beforehand.
Runnable r = () -> System.out.println(“This should be run in another thread”);
executorService.submit(r);
如果你想聲明一個(gè) lambda 表達(dá)式,可以不用像 Java 那樣聲明一個(gè)顯式類(lèi)型。
//Java 8 FunctionparseInt = s -> Integer.parseInt(s); //Scala val parseInt = (s: String) => s.toInt //or val parseInt:String => Int = s => s.toInt //or val parseInt:Function1[String, Int] = s => s.toInt
所以,在 Scala 中的確有多種辦法來(lái)聲明類(lèi)型。讓編譯器來(lái)執(zhí)行。那么 PentFunction 呢?
//Java 8 PentFunctionsumOfFive = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5; //Scala val sumOfFive = (i1: Int, i2: Int, i3: Int, i4: Int, i5: Int) => i1 + i2 + i3 + i4 + i5;
Scala 更短,因?yàn)椴恍枰暶鹘涌陬?lèi)型,而整數(shù)類(lèi)型在 Scala 中是 int。短不總意味著更好。Scala 的方法更好,不是因?yàn)槎蹋且驗(yàn)楦呖勺x性。類(lèi)型的上下文在參數(shù)列表中,可以很快找出參數(shù)類(lèi)型。如果還不確定,可以再參考以下代碼。
//Java 8 PentFunctionsumOfFive = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5; //Scala val sumOfFive = (i1: String, i2: Int, i3: Double, i4: Boolean, i5: String) => i1 + i2 + i3 + i4 + i5;
在 Scala 中,可以很明確地說(shuō)出 i3 類(lèi)型是 Double 型,但在 Java 8 中,還需要算算它是什么類(lèi)型。你可能爭(zhēng)辯說(shuō) Java 也可以,但出現(xiàn)這樣的狀況:
//Java 8 PentFunctionsumOfFive = (Integer i1, String i2, Integer i3, Double i4, Boolean i5) -> i1 + i2 + i3 + i4 + i5;
你必須一遍又一遍的重復(fù)下去。
除此之外,Java8 并沒(méi)有 PentFunction,需要自己定義。
//Java 8 @FunctionalInterface interface PentFunction { public R apply(A a, B b, C c, D d, E e); }
是不是意味著 Scala 就更好呢?在某些方面的確是。但也有很多地方 Scala 不如 Java。所以很難說(shuō)到底哪種更好,我之所以對(duì)兩者進(jìn)行比較,是因?yàn)?Scala 是一種函數(shù)語(yǔ)言,而 Java 8 支持一些函數(shù)特點(diǎn),所以得找函數(shù)語(yǔ)言來(lái)比較。由于 Scala 可以運(yùn)行在 JVM 上,用它來(lái)對(duì)比再好不過(guò)??赡苣銜?huì)在使用函數(shù)時(shí),Scala 有更簡(jiǎn)潔的語(yǔ)法和方法,這是因?yàn)樗緛?lái)就是函數(shù)語(yǔ)言,而 Java 的設(shè)計(jì)者在不破壞之前的基礎(chǔ)上拓展設(shè)計(jì),顯然會(huì)有更多限制。
盡管 Java在語(yǔ)法上與 lambda 表達(dá)式相比有一定局限性,但 Java8 也引進(jìn)了一些很酷的功能。例如,利用方法引用的特性通過(guò)重用現(xiàn)有方法使得編寫(xiě) lambda 表達(dá)式更簡(jiǎn)潔。更簡(jiǎn)潔嗎???
//Java 8 FunctionparseInt = s -> Integer.parseInt(s);
可以使用方法引用來(lái)重寫(xiě)函數(shù),如下所示
//Java 8 FunctionparseInt = Integer::parseInt;
還可以通過(guò)實(shí)例方法來(lái)使用方法引用。之后會(huì)在第二部分的 Stream API 中指出這種方法的可用性。
方法引用的構(gòu)造規(guī)則
1.(args) -> ClassName.staticMethod(args);
可以像這樣重寫(xiě)ClassName::staticMethod;
Function
2.(instance, args) -> instance.instanceMethod(args);
可以像這樣重寫(xiě) ClassName::instanceMethod;
BiFunction
3.(args) -> expression.instanceMethod(args);
可以像這樣重寫(xiě) expression::instanceMethod;
Function
你有沒(méi)有注意到規(guī)則2有點(diǎn)奇怪?有點(diǎn)混亂?盡管 indexOf 函數(shù)只需要1個(gè)參數(shù),但 BiFunction 的目標(biāo)類(lèi)型是需要2個(gè)參數(shù)。其實(shí),這種用法通常在 Stream API 中使用,當(dāng)看不到類(lèi)型名時(shí)才有意義。
pets.stream().map(Pet::getName).collect(toList()); // The signature of map() function can be derived as //Stream map(Function super Pet, ? extends String> mapper)
從規(guī)則3中,你可能會(huì)好奇能否用 lambda 表達(dá)式替換 new String()?
你可以用這種方法構(gòu)造一個(gè)對(duì)象
Supplier
那么可以這樣做嗎?
Function
不能。它不能編譯,編譯器會(huì)提示The target type of this expression must be a functional interface。錯(cuò)誤信息很容易引起誤解,而且似乎 Java 8通過(guò)泛型參數(shù)并不支持類(lèi)型接口。即使使用一個(gè) Functionalinterface 的實(shí)例(如前面提到的「STR」),也會(huì)出現(xiàn)另一個(gè)錯(cuò)誤The type Supplier
//Java FunctionindexOf = ((Supplier )String::new).get()::indexOf;
Java 8 是否支持 currying (partial function)?
的確可行,但你不能使用方法引用。你可以認(rèn)為是一個(gè) partial 函數(shù),但是它返回的是函數(shù)而不是結(jié)果。接著將要介紹使用 currying 的簡(jiǎn)單實(shí)例,但這個(gè)例子也可能行不通。在傳遞到函數(shù)之前,我們通常進(jìn)行參數(shù)處理。但無(wú)論如何,先看看如何利用 lambda 表達(dá)式實(shí)現(xiàn) partial 函數(shù)。假設(shè)你需要利用 currying 實(shí)現(xiàn)兩個(gè)整數(shù)相加的函數(shù)。
//Java IntFunctionadd = a -> b -> a + b; add.apply(2).applyAsInt(3);//the result is 4! I"m kidding it"s 5.
該函數(shù)可以同時(shí)采用兩個(gè)參數(shù)。
//Java Supplier> add = () -> (a, b) -> a + b; add.get().apply(2, 3);
現(xiàn)在,可以看看 Scala 方法。
//Scala val add = (a: Int) => (b:Int) => a + b add(1)(2) //Scala val add = () => (a: Int,b: Int) => a + b add2()(1,2)
因?yàn)轭?lèi)型引用和語(yǔ)法糖,Scala 的方法比 Java 更簡(jiǎn)短。在 Scala 中,你不需要在 Function trait 上調(diào)用 apply 方法,編譯器會(huì)即時(shí)地將()轉(zhuǎn)換為 apply 方法。
原文鏈接: https://dzone.com/articles/java-8-λe-vs-scalapart-i
OneAPM for Java 能夠深入到所有 Java 應(yīng)用內(nèi)部完成應(yīng)用性能管理和監(jiān)控,包括代碼級(jí)別性能問(wèn)題的可見(jiàn)性、性能瓶頸的快速識(shí)別與追溯、真實(shí)用戶(hù)體驗(yàn)監(jiān)控、服務(wù)器監(jiān)控和端到端的應(yīng)用性能管理。想閱讀更多技術(shù)文章,請(qǐng)?jiān)L問(wèn) OneAPM 官方博客。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/64768.html
摘要:初體驗(yàn)下面進(jìn)入本文的正題表達(dá)式。接下來(lái)展示表達(dá)式和其好基友的配合。吐槽一下方法引用表面上看起來(lái)方法引用和構(gòu)造器引用進(jìn)一步簡(jiǎn)化了表達(dá)式的書(shū)寫(xiě),但是個(gè)人覺(jué)得這方面沒(méi)有的下劃線(xiàn)語(yǔ)法更加通用。 感謝同事【天錦】的投稿。投稿請(qǐng)聯(lián)系 tengfei@ifeve.com 本文主要記錄自己學(xué)習(xí)Java8的歷程,方便大家一起探討和自己的備忘。因?yàn)楸救艘彩莿倓傞_(kāi)始學(xué)習(xí)Java8,所以文中肯定有錯(cuò)誤和理解偏...
摘要:比如對(duì)一個(gè)數(shù)據(jù)流進(jìn)行過(guò)濾映射以及求和運(yùn)算,通過(guò)使用延后機(jī)制,那么所有操作只要遍歷一次,從而減少中間調(diào)用。這里需知道中的元素都是延遲計(jì)算的,正因?yàn)榇?,能夠?jì)算無(wú)限數(shù)據(jù)流。 【編者按】在之前文章中,我們介紹了 Java 8和Scala的Lambda表達(dá)式對(duì)比。在本文,將進(jìn)行 Hussachai Puripunpinyo Java 和 Scala 對(duì)比三部曲的第二部分,主要關(guān)注 Stream...
摘要:我們的目標(biāo)是建立對(duì)每一種語(yǔ)言的認(rèn)識(shí),它們是如何進(jìn)化的,未來(lái)將走向何方。有點(diǎn)的味道是堅(jiān)持使用動(dòng)態(tài)類(lèi)型,但唯一還收到合理?yè)肀玫木幊陶Z(yǔ)言,然而一些在企業(yè)的大型團(tuán)隊(duì)中工作的開(kāi)發(fā)者擇認(rèn)為這會(huì)是的一個(gè)缺陷。 為什么我們需要如此多的JVM語(yǔ)言? 在2013年你可以有50中JVM語(yǔ)言的選擇來(lái)用于你的下一個(gè)項(xiàng)目。盡管你可以說(shuō)出一大打的名字,你會(huì)準(zhǔn)備為你的下一個(gè)項(xiàng)目選擇一種新的JVM語(yǔ)言么? 如今借助來(lái)自...
摘要:根據(jù)對(duì)社區(qū)和新特性的深刻理解,他創(chuàng)作了函數(shù)式編程一書(shū)。問(wèn)你在倫敦社區(qū)的經(jīng)歷是否幫助你創(chuàng)作了函數(shù)式編程這本書(shū)絕對(duì)是這樣。我認(rèn)為引入函數(shù)式編程會(huì)為很多編程任務(wù)提供方便。問(wèn)之前的是面向?qū)ο蟮?,現(xiàn)在全面支持函數(shù)式編程。 非商業(yè)轉(zhuǎn)載請(qǐng)注明作譯者、出處,并保留本文的原始鏈接:http://www.ituring.com.cn/article/199271 Richard Warburto...
摘要:是在嘗試讓擁有跟類(lèi)似的語(yǔ)法。在中使用,需要顯示得將集合轉(zhuǎn)成的步驟,而在中則免去了這樣的步驟。中的語(yǔ)句只能針對(duì)常量起作用,而使用模式匹配則可以對(duì)另一個(gè)函數(shù)的返回結(jié)果起作用,功能非常搶到。 Hystrix是Netflix開(kāi)源的限流、熔斷降級(jí)組件,去年發(fā)現(xiàn)Hystrix已經(jīng)不再更新了,而在github主頁(yè)上將我引導(dǎo)到了另一個(gè)替代項(xiàng)目——resilience4j,這個(gè)項(xiàng)目是基于Java 8開(kāi)發(fā)...
閱讀 2379·2021-11-08 13:13
閱讀 1323·2021-10-09 09:41
閱讀 1766·2021-09-02 15:40
閱讀 3255·2021-08-17 10:13
閱讀 2603·2019-08-29 16:33
閱讀 3203·2019-08-29 13:17
閱讀 3189·2019-08-29 11:00
閱讀 3349·2019-08-26 13:40