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

資訊專欄INFORMATION COLUMN

Java 8 vs. Scala(一): Lambda表達(dá)式

yuanxin / 1713人閱讀

摘要:編程語言將函數(shù)作為一等公民,函數(shù)可以被作為參數(shù)或者返回值傳遞,因為它被視為對象。是表示已注釋接口是函數(shù)接口的注釋。如果一個函數(shù)有一個或多個參數(shù)并且有返回值呢為了解決這個問題,提供了一系列通用函數(shù)接口,在包里。

【編者按】雖然 Java 深得大量開發(fā)者喜愛,但是對比其他現(xiàn)代編程語言,其語法確實略顯冗長。但是通過 Java8,直接利用 lambda 表達(dá)式就能編寫出既可讀又簡潔的代碼。作者 Hussachai Puripunpinyo 的軟件工程師,作者通過對比 Java 8和 Scala,對性能和表達(dá)方面的差異進行了分析,并且深入討論關(guān)于 Stream API 的區(qū)別,本文由 OneAPM 工程師編譯整理。

數(shù)年等待,Java 8 終于添加了高階函數(shù)這個特性。本人很喜歡 Java,但不得不承認(rèn),相比其他現(xiàn)代編程語言,Java 語法非常冗長。然而通過 Java8,直接利用 lambda 表達(dá)式就能編寫出既可讀又簡潔的代碼(有時甚至比傳統(tǒng)方法更具可讀性)。

Java 8于2014年3月3日發(fā)布,但筆者最近才有機會接觸。因為筆者也很熟悉 Scala,所以就產(chǎn)生了對比 Java 8和Scala 在表達(dá)性和性能方面的差異,比較將圍繞 Stream API 展開,同時也會介紹如何使用 Stream API 來操作集合。

由于文章太長,所以分以下三個部分詳細(xì)敘述。

Part 1.Lambda 表達(dá)式

Part 2. Stream API vs Scala collection API

Part 3. Trust no one, bench everything(引用自sbt-jmh)

首先,我們來了解下 Java 8的 lambda 表達(dá)式,雖然不知道即使表達(dá)式部分是可替代的,他們卻稱之為 lambda 表達(dá)式。這里完全可以用聲明來代替表達(dá)式,然后說 Java 8還支持 lambda 聲明。編程語言將函數(shù)作為一等公民,函數(shù)可以被作為參數(shù)或者返回值傳遞,因為它被視為對象。Java是一種靜態(tài)的強類型語言。所以,函數(shù)必須有類型,因此它也是一個接口。

另一方面,lambda 函數(shù)就是實現(xiàn)了函數(shù)接口的一個類。無需創(chuàng)建這個函數(shù)的類,編譯器會直接實現(xiàn)。不幸的是,Java 沒有 Scala 那樣高級的類型接口。如果你想聲明一個 lambda 表達(dá)式,就必須指定目標(biāo)類型。實際上,由于 Java 必須保持向后兼容性,這也是可理解的,而且就目前來說 Java 完成得很好。例如,Thread.stop() 在 JDK 1.0版時發(fā)布,已過時了十多年,但即便到今天仍然還在使用。所以,不要因為語言 XYZ 的語法(或方法)更好,就指望 Java 從根本上改變語法結(jié)構(gòu)。

所以,Java 8的語言設(shè)計師們奇思妙想,做成函數(shù)接口!函數(shù)接口是只有一個抽象方法的接口。要知道,大多數(shù)回調(diào)接口已經(jīng)滿足這一要求。因此,我們可以不做任何修改重用這些接口。@FunctionalInterface 是表示已注釋接口是函數(shù)接口的注釋。此注釋是可選的,除非有檢查要求,否則不用再進行處理。

請記住,lambda 表達(dá)式必須定義類型,而該類型必須只有一個抽象方法。

//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”);

如果一個函數(shù)有一個或多個參數(shù)并且有返回值呢?為了解決這個問題,Java 8提供了一系列通用函數(shù)接口,在java.util.function包里。

//Java 8
Function parseInt = (String s) -> Integer.parseInt(s);

該參數(shù)類型可以從函數(shù)中推斷,就像 Java7中的diamond operator,所以可以省略。我們可以重寫該函數(shù),如下所示:

//Java 8
Function parseInt = s -> Integer.parseInt(s);

如果一個函數(shù)有兩個參數(shù)呢?無需擔(dān)心,Java 8 中有 BiFunction。

//Java 8
BiFunction multiplier = 
  (i1, i2) -> i1 * i2; //you can’t omit parenthesis here!

如果一個函數(shù)接口有三個參數(shù)呢?TriFunction?語言設(shè)計者止步于 BiFunction。否則,可能真會有 TriFunction、quadfunction、pentfunction 等。解釋一下,筆者是采用 IUPAC 規(guī)則來命名函數(shù)的。然后,可以按如下所示定義 TriFunction。

//Java 8
@FunctionalInterface
interface TriFunction {  
  public R apply(A a, B b, C c);
}

然后導(dǎo)入接口,并把它當(dāng)作 lambda 表達(dá)式類型使用。

//Java 8
TriFunction sumOfThree 
  = (i1, i2, i3) -> i1 + i2 + i3;

這里你應(yīng)該能理解為什么設(shè)計者止步于 BiFunction。

如果還沒明白,不妨看看 PentFunction,假設(shè)我們在其他地方已經(jīng)定義了 PentFunction。

//Java 8
PentFunction 
  sumOfFive = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5;

你知道 Ennfunction 是多長嗎?(拉丁語中,enn 表示9)你必須申報 10 種類型(前 9 個是參數(shù),最后一個是返回類型),大概整行都只有類型了。那么聲明一個類型是否有必要呢?答案是肯定的。(這也是為什么筆者認(rèn)為 Scala 的類型接口比 Java 的更好)

Scala 也有其 lambda 表達(dá)式類型。在 Scala 中,你可以創(chuàng)建有22個參數(shù)的 lambda 表達(dá)式,意味著 Scala 有每個函數(shù)的類型(Function0、Function1、……Function22)。函數(shù)類型在 Scala 函數(shù)中是一個 Trait,Trait 就像 Java 中的抽象類,但可以當(dāng)做混合類型使用。如果還需要22個以上的參數(shù),那大概是你函數(shù)的設(shè)計有問題。必須要考慮所傳遞的一組參數(shù)的類型。在此,筆者將不再贅述關(guān)于 Lambda 表達(dá)式的細(xì)節(jié)。

下面來看看Scala的其他內(nèi)容。Scala 也是類似 Java 的靜態(tài)強類型語言,但它一開始就是函數(shù)語言。因此,它能很好地融合面向?qū)ο蠛秃瘮?shù)編程。由于 Scala 和 Java 所采用的方法不同,這里不能給出 Runnable 的 Scala 實例。Scala 有自己解決問題的方法,所以接下來會詳細(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);

如果你想聲明一個 lambda 表達(dá)式,可以不用像 Java 那樣聲明一個顯式類型。

//Java 8
Function parseInt = 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 中的確有多種辦法來聲明類型。讓編譯器來執(zhí)行。那么 PentFunction 呢?

//Java 8
PentFunction sumOfFive 
  = (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 更短,因為不需要聲明接口類型,而整數(shù)類型在 Scala 中是 int。短不總意味著更好。Scala 的方法更好,不是因為短,而是因為更具可讀性。類型的上下文在參數(shù)列表中,可以很快找出參數(shù)類型。如果還不確定,可以再參考以下代碼。

//Java 8
PentFunction 
  sumOfFive = (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 中,可以很明確地說出 i3 類型是 Double 型,但在 Java 8 中,還需要算算它是什么類型。你可能爭辯說 Java 也可以,但出現(xiàn)這樣的狀況:

//Java 8
PentFunction sumOfFive 
  = (Integer i1, String i2, Integer i3, Double i4, Boolean i5) 
  -> i1 + i2 + i3 + i4 + i5;

你必須一遍又一遍的重復(fù)下去。

除此之外,Java8 并沒有 PentFunction,需要自己定義。

//Java 8
@FunctionalInterface
interface PentFunction {  
  public R apply(A a, B b, C c, D d, E e);
}

是不是意味著 Scala 就更好呢?在某些方面的確是。但也有很多地方 Scala 不如 Java。所以很難說到底哪種更好,我之所以對兩者進行比較,是因為 Scala 是一種函數(shù)語言,而 Java 8 支持一些函數(shù)特點,所以得找函數(shù)語言來比較。由于 Scala 可以運行在 JVM 上,用它來對比再好不過??赡苣銜谑褂煤瘮?shù)時,Scala 有更簡潔的語法和方法,這是因為它本來就是函數(shù)語言,而 Java 的設(shè)計者在不破壞之前的基礎(chǔ)上拓展設(shè)計,顯然會有更多限制。

盡管 Java在語法上與 lambda 表達(dá)式相比有一定局限性,但 Java8 也引進了一些很酷的功能。例如,利用方法引用的特性通過重用現(xiàn)有方法使得編寫 lambda 表達(dá)式更簡潔。更簡潔嗎???

//Java 8
Function parseInt = s -> Integer.parseInt(s);

可以使用方法引用來重寫函數(shù),如下所示

//Java 8
Function parseInt = Integer::parseInt;

還可以通過實例方法來使用方法引用。之后會在第二部分的 Stream API 中指出這種方法的可用性。

方法引用的構(gòu)造規(guī)則

1.(args) -> ClassName.staticMethod(args);

可以像這樣重寫ClassName::staticMethod;

Function intToStr = String::valueOf;

2.(instance, args) -> instance.instanceMethod(args);

可以像這樣重寫 ClassName::instanceMethod;

BiFunction indexOf = String::indexOf;

3.(args) -> expression.instanceMethod(args);

可以像這樣重寫 expression::instanceMethod;

FunctionindexOf = new String()::indexOf;

你有沒有注意到規(guī)則2有點奇怪?有點混亂?盡管 indexOf 函數(shù)只需要1個參數(shù),但 BiFunction 的目標(biāo)類型是需要2個參數(shù)。其實,這種用法通常在 Stream API 中使用,當(dāng)看不到類型名時才有意義。

pets.stream().map(Pet::getName).collect(toList());
// The signature of map() function can be derived as
//  Stream map(Function mapper)

從規(guī)則3中,你可能會好奇能否用 lambda 表達(dá)式替換 new String()?

你可以用這種方法構(gòu)造一個對象

Supplier str =String::new;

那么可以這樣做嗎?

Function,Integer> indexOf = (String::new)::indexOf;

不能。它不能編譯,編譯器會提示The target type of this expression must be a functional interface。錯誤信息很容易引起誤解,而且似乎 Java 8通過泛型參數(shù)并不支持類型接口。即使使用一個 Functionalinterface 的實例(如前面提到的「STR」),也會出現(xiàn)另一個錯誤The type Supplier does not define indexOf(Supplier) that is applicable here。String::new 的函數(shù)接口是 Supplier,而且它只有方法命名為 get()。indexOf 是一個屬于 String 對象的實例方法。因此,必須重寫這段代碼,如下所示。

//Java
Function indexOf =          ((Supplier)String::new).get()::indexOf;

Java 8 是否支持 currying (partial function)?

的確可行,但你不能使用方法引用。你可以認(rèn)為是一個 partial 函數(shù),但是它返回的是函數(shù)而不是結(jié)果。接著將要介紹使用 currying 的簡單實例,但這個例子也可能行不通。在傳遞到函數(shù)之前,我們通常進行參數(shù)處理。但無論如何,先看看如何利用 lambda 表達(dá)式實現(xiàn) partial 函數(shù)。假設(shè)你需要利用 currying 實現(xiàn)兩個整數(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ù)。

//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)

因為類型引用和語法糖,Scala 的方法比 Java 更簡短。在 Scala 中,你不需要在 Function trait 上調(diào)用 apply 方法,編譯器會即時地將()轉(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)控,包括代碼級別性能問題的可見性、性能瓶頸的快速識別與追溯、真實用戶體驗監(jiān)控、服務(wù)器監(jiān)控和端到端的應(yīng)用性能管理。想閱讀更多技術(shù)文章,請訪問 OneAPM 官方博客。

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

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

相關(guān)文章

  • 轉(zhuǎn) | Java8初體驗(lambda達(dá)式語法

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

    Lucky_Boy 評論0 收藏0
  • Java 8 vs. Scala(二):Stream vs. Collection

    摘要:比如對一個數(shù)據(jù)流進行過濾映射以及求和運算,通過使用延后機制,那么所有操作只要遍歷一次,從而減少中間調(diào)用。這里需知道中的元素都是延遲計算的,正因為此,能夠計算無限數(shù)據(jù)流。 【編者按】在之前文章中,我們介紹了 Java 8和Scala的Lambda表達(dá)式對比。在本文,將進行 Hussachai Puripunpinyo Java 和 Scala 對比三部曲的第二部分,主要關(guān)注 Stream...

    GeekGhc 評論0 收藏0
  • JVM 平臺上的各種語言的開發(fā)指南[z]

    摘要:我們的目標(biāo)是建立對每一種語言的認(rèn)識,它們是如何進化的,未來將走向何方。有點的味道是堅持使用動態(tài)類型,但唯一還收到合理擁泵的編程語言,然而一些在企業(yè)的大型團隊中工作的開發(fā)者擇認(rèn)為這會是的一個缺陷。 為什么我們需要如此多的JVM語言? 在2013年你可以有50中JVM語言的選擇來用于你的下一個項目。盡管你可以說出一大打的名字,你會準(zhǔn)備為你的下一個項目選擇一種新的JVM語言么? 如今借助來自...

    phodal 評論0 收藏0
  • Java 8函數(shù)式編程》作者Richard Warbourton:Java的亮點不是語言本身

    摘要:根據(jù)對社區(qū)和新特性的深刻理解,他創(chuàng)作了函數(shù)式編程一書。問你在倫敦社區(qū)的經(jīng)歷是否幫助你創(chuàng)作了函數(shù)式編程這本書絕對是這樣。我認(rèn)為引入函數(shù)式編程會為很多編程任務(wù)提供方便。問之前的是面向?qū)ο蟮?,現(xiàn)在全面支持函數(shù)式編程。 非商業(yè)轉(zhuǎn)載請注明作譯者、出處,并保留本文的原始鏈接:http://www.ituring.com.cn/article/199271 Richard Warburto...

    mzlogin 評論0 收藏0
  • vavr:讓你像寫Scala樣寫Java

    摘要:是在嘗試讓擁有跟類似的語法。在中使用,需要顯示得將集合轉(zhuǎn)成的步驟,而在中則免去了這樣的步驟。中的語句只能針對常量起作用,而使用模式匹配則可以對另一個函數(shù)的返回結(jié)果起作用,功能非常搶到。 Hystrix是Netflix開源的限流、熔斷降級組件,去年發(fā)現(xiàn)Hystrix已經(jīng)不再更新了,而在github主頁上將我引導(dǎo)到了另一個替代項目——resilience4j,這個項目是基于Java 8開發(fā)...

    andycall 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<