欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入學(xué)習(xí) Java 中的 Lambda

 更新時間:2019年06月13日 10:30:07   作者:Tocy  
Lambda表達式是Java SE 8中一個重要的新特性。lambda表達式允許你通過表達式來代替功能接口。 lambda表達式就和方法一樣,它提供了一個正常的參數(shù)列表和一個使用這些參數(shù)的主體(body,可以是一個表達式或一個代碼塊)。,需要的朋友可以參考下

前言

我花了相當(dāng)多的閱讀和編碼時間才最終理解Java Lambdas如何在概念上正常工作的。我閱讀的大多數(shù)教程和介紹都遵循自頂向下的方法,從用例開始,最后以概念性問題結(jié)束。在這篇文章中,我想提供一個自下而上的解釋,從其他已建立的Java概念中推導(dǎo)出Lambdas的概念。

首先介紹下方法的類型化,這是支持方法作為一流公民的先決條件。基于此,Lambdas的概念是被以匿名類用法的進化和特例提出的。所有這一切都通過實現(xiàn)和使用高階函數(shù)映射來說明。

這篇文章的主要受眾是那些已掌握函數(shù)式編程基礎(chǔ)的人,以及那些想從概念上理解Lambdas如何嵌入Java語言的人。

方法類型

從Java 8起方法就是一等公民了。按照標(biāo)準(zhǔn)的定義,編程語言中的一等公民是一個具有下列功能的實體,

  • 可以作為參數(shù)進行傳遞,
  • 可以作為方法的返回值
  • 可以賦值給一個變量.

在Java中,每一個參數(shù)、返回值或變量都是有類型的,因此每個一等公民都必須是有類型的。Java中的一種類型可以是以下內(nèi)容之一:

  • 一種內(nèi)建類型 (比如 int 或者 double)
  • 一個類 (比如ArrayList)
  • 一個接口 (比如 Iterable)

方法是通過接口進行定義類型的。它們不隱式的實現(xiàn)特定接口,但是在必要的時候,如果一個方法符合一個接口,那么在編譯期間,Java編譯器會對其進行隱式的檢查。舉個例子說明:

class LambdaMap {
static void oneStringArgumentMethod(String arg) {
System.out.println(arg);
}
}

關(guān)于oneStringArgumentMethod函數(shù)的類型,與之相關(guān)的有:它的的函數(shù)是靜態(tài)的,返回類型是void,它接受一個String類型的參數(shù)。一個靜態(tài)函數(shù)符合包含一個apply函數(shù)的接口,apply函數(shù)的簽名相應(yīng)地符合這個靜態(tài)函數(shù)的簽名。

oneStringArgumentMethod函數(shù)對應(yīng)的接口因此必須符合下列標(biāo)準(zhǔn)。

  • 它必須包含一個名為apply的函數(shù)。
  • 函數(shù)返回類型必須是void。
  • 函數(shù)必須接受一個String類型可以轉(zhuǎn)換到的對象的參數(shù)。

在符合這個標(biāo)準(zhǔn)的接口之中,下面的這個是最明確的:

interface OneStringArgumentInterface {
void apply(String arg);
}

利用這個接口,函數(shù)可以分配給一個變量:

OneStringArgumentInterface meth = LambdaMap::oneStringArgumentMethod;

用這種方法使用接口作為類型,函數(shù)可以借此被分配給變量,傳遞參數(shù)并且從函數(shù)返回:

static OneStringArgumentInterface getWriter() {
return LambdaMap::oneStringArgumentMethod;
}
static void write(OneStringArgumentInterface writer, String msg) {
writer.apply(msg);
}

最終函數(shù)是一等公民。

泛型函數(shù)類型

就像使用集合一樣,泛型為函數(shù)類型增加了大量的功能和靈活性。實現(xiàn)功能上的算法而不考慮類型相關(guān)信息,泛型函數(shù)類型使其變?yōu)榭赡?。在對map函數(shù)的實現(xiàn)中,會在下面用到這種功能。

在這提供的OneStringArgumentInterface一個泛型版本:

interface OneArgumentInterface<T> {
void apply(T arg);
}

OneStringArgumentInterface函數(shù)可以被分配給它:

OneArgumentInterface<String> meth = LambdaMap::oneStringArgumentMethod;

通過使用泛型函數(shù)類型,它現(xiàn)在可以以一種通用的方法實現(xiàn)算法,就像它在集合中使用的一樣:

static <T> void applyArgument(OneArgumentInterface<T> meth, T arg) {
meth.apply(arg);
}

上面的函數(shù)并沒有什么用,然而它至少可以提出一個想法:對函數(shù)作為第一個類成員的支持怎樣可以形成非常簡潔且靈活的代碼:

applyArgument(Lambda::oneStringArgumentMethod, "X");

實現(xiàn)map

在諸多高階函數(shù)中,map是最經(jīng)典的. map的第一個參數(shù)是函數(shù),該函數(shù)可以接收一個參數(shù)并返回一個值;第二個參數(shù)是值列表. map使用傳入的函數(shù)處理值列表的每一項,然后返回一個新的值列表。下面Python的代碼片段,可以很好的說明map的用法:

>>> map(math.sqrt, [1, 4, 9, 16])
[1.0, 2.0, 3.0, 4.0]

在本節(jié)的后續(xù)內(nèi)容中,將給出該函數(shù)的Java實現(xiàn)。Java 8已經(jīng)通過Stream提供了該函數(shù)。因為主要出于教學(xué)目的,所以,本節(jié)中給出的實現(xiàn)特意保持簡單,僅限于List對象使用。

與Python不同,在Java中必須首先考慮map第一個參數(shù)的類型:一個可以接收一個參數(shù)并返回一個值的方法。參數(shù)的類型和返回值的類型可以不同。下面接口符合這個預(yù)期,顯然,I表示參數(shù)(入?yún)ⅲ琌表示返回值(出參):

interface MapFunction<I, O> {
O apply(I in);
}

泛型map方法的實現(xiàn),變得驚人的簡單明了:

static <I, O> List<O> map(MapFunction<I, O> func, List<I> input) {
List<O> out = new ArrayList<>();
for (I in : input) {
out.add(func.apply(in));
}
return out;
}

1.創(chuàng)建新的返回值列表out(用于保存O類型的對象).

2.通過遍歷input,func處理列表的每一項,并將返回值添加到out中。

3.返回out.

下面是實際使用map方法的實例:

MapFunction<Integer, Double> func = Math::sqrt;
List<Double> output = map(func, Arrays.asList(1., 4., 9., 16.));
System.out.println(output);

在Python one-liner的推動下,可以用更簡潔的方法表達:

System.out.println(map(Math::sqrt, Arrays.asList(1., 4., 9., 16.)));

Java畢竟不是Python...

Lambdas來了!

讀者可能會注意到,還沒有提到Lambdas。這是由于采用了“自下而上”的方式描述,現(xiàn)在基礎(chǔ)已基本建立,Lambdas將在后續(xù)的章節(jié)中介紹。

下面的用例作為基礎(chǔ):一個double類型的list,表示半徑,然后得到一個列表,表示圓面積。map方法就是為此任務(wù)預(yù)先準(zhǔn)備的。計算圓面積的公式是眾所周知的:

A = r2π

應(yīng)用這個公式的方法很容易實現(xiàn):

static Double circleArea(Double radius) {
return Math.pow(radius, 2) * Math.PI;
}

這個方法現(xiàn)在可以用作map方法的第一個參數(shù):

System.out.println(
map(LambdaMap::circleArea,
Arrays.asList(1., 4., 9., 16.)));

如果circleArea方法只需要這一次, 沒有道理把類接口被他弄得亂七八糟,也沒有道理將實現(xiàn)和真正使用它的地方分離。最佳實踐是使用用匿名內(nèi)部類??梢钥吹剑瑢嵗粋€實現(xiàn)MapFunction接口的匿名內(nèi)部類可以很好的完成這個任務(wù):

System.out.println(
map(new MapFunction<Double, Double>() {
public Double apply(Double radius) {
return Math.sqrt(radius) * Math.PI;
}
},
Arrays.asList(1., 2., 3., 4.)));

這看起來很漂亮,但是很多人會認(rèn)為函數(shù)式的解決方案更清晰,更具可讀性:

List<Double> out = new ArrayList<>();
for (Double radius : Arrays.asList(1., 2., 3., 4.)) {
out.add(Math.sqrt(radius) * Math.PI);
}
System.out.println(out);

到目前為止,最后是使用Lambda表達式。 讀者應(yīng)該注意Lambda如何取代上面提到的匿名類:

System.out.println(
map(radius -> { return Math.sqrt(radius) * Math.PI; },
Arrays.asList(1., 2., 3., 4.)));

這看起來簡潔明了 - 請注意 Lambda 表達式如何缺省任何明確的類型信息。 沒有顯式模板實例化,沒有方法簽名。

Lambda表達式由兩部分組成,這兩部分被->分隔。第一部分是參數(shù)列表,第二部分是實際實現(xiàn)。

Lambda表達式和匿名內(nèi)部類作用完全相同,然而它摒棄了許多編譯器可以自動推斷的樣板代碼。讓我們再次比較這兩種方式,然后分析編譯器為開發(fā)人員節(jié)省了哪些工作。

MapFunction<Double, Double> functionLambda =
radius -> Math.sqrt(radius) * Math.PI;
MapFunction<Double, Double> functionClass =
new MapFunction<Double, Double>() {
public Double apply(Double radius) {
return Math.sqrt(radius) * Math.PI;
}
};
  • 對于Lambda實現(xiàn)來說,只有一個表達式,返回語句和花括號可以省略。這使得代碼更簡短。
  • Lambda表達式的返回值類型是從Lambda實現(xiàn)推斷出來的。
  • 對于參數(shù)類型,我不完全確定,但我認(rèn)為必須從Lambda表達式所處的上下文中推斷出參數(shù)類型。
  • 最后編譯器必須檢查返回值類型是否與Lambda的上下文匹配,以及參數(shù)類型是否與Lambda實現(xiàn)匹配。

這一切都可以在編譯期間完成,根本沒有運行時開銷。

結(jié)語

總而言之,Java中的Lambdas的概念是整潔的。我支持編寫更簡潔、更清晰的代碼,并讓程序員免于編寫可由編譯器自動推斷的架手架代碼。它是語法糖,如上所述,它只不過是使用匿名類也能實現(xiàn)的功能。然而,我會說它是非常甜的語法糖。

另一方面,Lambdas還支持更加混淆以及難以調(diào)試的代碼。Python社區(qū)很早就意識到了這一點 - 雖然Python也有Lambda,但它若被廣泛使用則通常被認(rèn)為是不好的風(fēng)格(當(dāng)嵌套函數(shù)可以被使用時,它并不難于規(guī)避)。對于Java來說,我會給出類似的建議。

毫無疑問,在某些情況下,使用Lambdas會導(dǎo)致代碼大大縮減并更易讀,尤其在與流有關(guān)時。在其他情況下,如果采取更保守的做法和最佳實踐,另外一種方法可能會是更好的替代。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java zxing生成條形碼和二維嗎代碼實例

    Java zxing生成條形碼和二維嗎代碼實例

    這篇文章主要介紹了java zxing生成條形碼和二維嗎代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • SpringBoot中的多RabbitMQ數(shù)據(jù)源配置實現(xiàn)

    SpringBoot中的多RabbitMQ數(shù)據(jù)源配置實現(xiàn)

    本篇博客將介紹如何在 Spring Boot 中配置和管理多個 RabbitMQ 數(shù)據(jù)源,以滿足不同的應(yīng)用需求,具有一定的參考價值,感興趣的可以了解一下
    2023-09-09
  • Java生成表格圖片的實例代碼

    Java生成表格圖片的實例代碼

    這篇文章主要介紹了Java生成表格圖片的實例代碼,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-09-09
  • 淺談Hibernate n+1問題

    淺談Hibernate n+1問題

    這篇文章主要介紹了淺談Hibernate n+1問題,怎么解決n+1問題,文中也作了簡要分析,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • 使用mybatis框架連接mysql數(shù)據(jù)庫的超詳細(xì)步驟

    使用mybatis框架連接mysql數(shù)據(jù)庫的超詳細(xì)步驟

    MyBatis是目前java項目連接數(shù)據(jù)庫的最流行的orm框架了,下面這篇文章主要給大家介紹了關(guān)于使用mybatis框架連接mysql數(shù)據(jù)庫的超詳細(xì)步驟,文中通過實例代碼和圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04
  • java中@JSONField和@JsonProperty注解的使用說明及對比

    java中@JSONField和@JsonProperty注解的使用說明及對比

    @JSONField與@JsonProperty隸屬兩個不同的包,前者是阿里系的fastjson包,后者是spring?boot官方使用的jackson包,本文主要介紹了java中@JSONField和@JsonProperty注解的使用說明及對比,感興趣的可以了解一下
    2023-11-11
  • spring boot activiti工作流的搭建與簡單使用

    spring boot activiti工作流的搭建與簡單使用

    這篇文章主要給大家介紹了關(guān)于spring boot activiti工作流的搭建與簡單使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • Java復(fù)合語句的使用方法詳解

    Java復(fù)合語句的使用方法詳解

    這篇文章主要介紹了Java編程中復(fù)合語句,結(jié)合相關(guān)的具體實例介紹了其用法,需要的朋友可以參考下
    2017-09-09
  • 如何使用ActiveMQ中間件方式發(fā)送郵件

    如何使用ActiveMQ中間件方式發(fā)送郵件

    這篇文章主要介紹了如何使用ActiveMQ中間件方式發(fā)送郵件的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java 對數(shù)和指數(shù)計算方式

    java 對數(shù)和指數(shù)計算方式

    這篇文章主要介紹了java 對數(shù)和指數(shù)計算方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08

最新評論