真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java8:一文帶你掌握Lambda表達式

本期教程將介紹 Java 8 新增的 Lambda 表達式,包括 Lambda 表達式的常見用法以及方法引用的用法,并對 Lambda 表達式的原理進行分析,最后對 Lambda 表達式的優(yōu)缺點進行一個總結(jié)。

創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供古藺網(wǎng)站建設(shè)、古藺做網(wǎng)站、古藺網(wǎng)站設(shè)計、古藺網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、古藺企業(yè)網(wǎng)站模板建站服務(wù),十載古藺做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。

概述

Java 8 引入的 Lambda 表達式的主要作用就是簡化部分匿名內(nèi)部類的寫法。

能夠使用 Lambda 表達式的一個重要依據(jù)是必須有相應的函數(shù)接口。所謂函數(shù)接口,是指內(nèi)部有且僅有一個抽象方法的接口。

Lambda 表達式的另一個依據(jù)是類型推斷機制。在上下文信息足夠的情況下,編譯器可以推斷出參數(shù)表的類型,而不需要顯式指名。

常見用法

2.1 無參函數(shù)的簡寫

無參函數(shù)就是沒有參數(shù)的函數(shù),例如 Runnable 接口的 run() 方法,其定義如下:

@FunctionalInterface
public?interface?Runnable?{
?public?abstract?void?run();
}

在 Java 7 及之前版本,我們一般可以這樣使用:

new?Thread(new?Runnable()?{
?@Override
?public?void?run()?{
?System.out.println("Hello");
?System.out.println("Jimmy");
?}
}).start();

從 Java 8 開始,無參函數(shù)的匿名內(nèi)部類可以簡寫成如下方式:

()?->?{
?執(zhí)行語句
}

這樣接口名和函數(shù)名就可以省掉了。那么,上面的示例可以簡寫成:

new?Thread(()?->?{
?System.out.println("Hello");
?System.out.println("Jimmy");
}).start();

當只有一條語句時,我們還可以對代碼塊進行簡寫,格式如下:

()?->?表達式

注意這里使用的是表達式,并不是語句,也就是說不需要在末尾加分號。

那么,當上面的例子中執(zhí)行的語句只有一條時,可以簡寫成這樣:

new?Thread(()?->?System.out.println("Hello")).start();

2.2 單參函數(shù)的簡寫

單參函數(shù)是指只有一個參數(shù)的函數(shù)。例如 View 內(nèi)部的接口 OnClickListener 的方法 onClick(View v),其定義如下:

public?interface?OnClickListener?{
?/**
?*?Called?when?a?view?has?been?clicked.
?*
?*?@param?v?The?view?that?was?clicked.
?*/
?void?onClick(View?v);
}

在 Java 7 及之前的版本,我們通??赡軙@么使用:

view.setOnClickListener(new?View.OnClickListener()?{
?@Override
?public?void?onClick(View?v)?{
?v.setVisibility(View.GONE);
?}
});

從 Java 8 開始,單參函數(shù)的匿名內(nèi)部類可以簡寫成如下方式:

([類名?]變量名)?->?{
?執(zhí)行語句
}

其中類名是可以省略的,因為 Lambda 表達式可以自己推斷出來。那么上面的例子可以簡寫成如下兩種方式:

view.setOnClickListener((View?v)?->?{
?v.setVisibility(View.GONE);
});
view.setOnClickListener((v)?->?{
?v.setVisibility(View.GONE);
});

單參函數(shù)甚至可以把括號去掉,官方也更建議使用這種方式:

變量名?->?{
?執(zhí)行語句
}

那么,上面的示例可以簡寫成:

view.setOnClickListener(v?->?{
?v.setVisibility(View.GONE);
});

當只有一條語句時,依然可以對代碼塊進行簡寫,格式如下:

([類名?]變量名)?->?表達式

類名和括號依然可以省略,如下:

變量名?->?表達式

那么,上面的示例可以進一步簡寫成:

view.setOnClickListener(v?->?v.setVisibility(View.GONE));

2.3 多參函數(shù)的簡寫

多參函數(shù)是指具有兩個及以上參數(shù)的函數(shù)。例如,Comparator 接口的 compare(T o1, T o2) 方法就具有兩個參數(shù),其定義如下:

@FunctionalInterfacepublic?interface?Comparator{
?int?compare(T?o1,?T?o2);
}

在 Java 7 及之前的版本,當我們對一個集合進行排序時,通常可以這么寫:

Listlist?=?Arrays.asList(1,?2,?3);Collections.sort(list,?new?Comparator()?{
?@Override
?public?int?compare(Integer?o1,?Integer?o2)?{
?return?o1.compareTo(o2);
?}
});

從 Java 8 開始,多參函數(shù)的匿名內(nèi)部類可以簡寫成如下方式:

([類名1?]變量名1,?[類名2?]變量名2[,?...])?->?{
?執(zhí)行語句
}

同樣類名可以省略,那么上面的例子可以簡寫成:

Collections.sort(list,?(Integer?o1,?Integer?o2)?->?{
?return?o1.compareTo(o2);
});
Collections.sort(list,?(o1,?o2)?->?{
?return?o1.compareTo(o2);
});

當只有一條語句時,依然可以對代碼塊進行簡寫,格式如下:

([類名1?]變量名1,?[類名2?]變量名2[,?...])?->?表達式

此時類名也是可以省略的,但括號不能省略。如果這條語句需要返回值,那么 return 關(guān)鍵字是不需要寫的。

因此,上面的示例可以進一步簡寫成:

Collections.sort(list,?(o1,?o2)?->?o1.compareTo(o2));

最后呢,這個示例還可以簡寫成這樣:

Collections.sort(list,?Integer::compareTo);

咦,這是什么特性?這就是我們下面要講的內(nèi)容:方法引用。

三. 方法引用

方法引用也是一個語法糖,可以用來簡化開發(fā)。http://dalian.qd8.com.cn/yiyao/xinxi21_3710011.html

在我們使用 Lambda 表達式的時候,如果“->”的右邊要執(zhí)行的表達式只是調(diào)用一個類已有的方法,那么就可以用「方法引用」來替代 Lambda 表達式。

方法引用可以分為 4 類:

  • 引用靜態(tài)方法;

  • 引用對象的方法;

  • 引用類的方法;

  • 引用構(gòu)造方法。

下面按照這 4 類分別進行闡述。

Java 8:一文帶你掌握 Lambda 表達式

3.1 引用靜態(tài)方法

當我們要執(zhí)行的表達式是調(diào)用某個類的靜態(tài)方法,并且這個靜態(tài)方法的參數(shù)列表和接口里抽象函數(shù)的參數(shù)列表一一對應時,我們可以采用引用靜態(tài)方法的格式。

假如 Lambda 表達式符合如下格式:

([變量1,?變量2,?...])?->?類名.靜態(tài)方法名([變量1,?變量2,?...])

我們可以簡寫成如下格式:

類名::靜態(tài)方法名

注意這里靜態(tài)方法名后面不需要加括號,也不用加參數(shù),因為編譯器都可以推斷出來。下面我們繼續(xù)使用 2.3 節(jié)的示例來進行說明。

首先創(chuàng)建一個工具類,代碼如下:

public?class?Utils?{
?public?static?int?compare(Integer?o1,?Integer?o2)?{
?return?o1.compareTo(o2);
?}
}

注意這里的 compare() 函數(shù)的參數(shù)和 Comparable 接口的 compare() 函數(shù)的參數(shù)是一一對應的。然后一般的 Lambda 表達式可以這樣寫:

Collections.sort(list,?(o1,?o2)?->?Utils.compare(o1,?o2));

如果采用方法引用的方式,可以簡寫成這樣:

Collections.sort(list,?Utils::compare);

3.2 引用對象的方法

當我們要執(zhí)行的表達式是調(diào)用某個對象的方法,并且這個方法的參數(shù)列表和接口里抽象函數(shù)的參數(shù)列表一一對應時,我們就可以采用引用對象的方法的格式。

假如 Lambda 表達式符合如下格式:

([變量1,?變量2,?...])?->?對象引用.方法名([變量1,?變量2,?...])

我們可以簡寫成如下格式:

對象引用::方法名

下面我們繼續(xù)使用 2.3 節(jié)的示例來進行說明。首先創(chuàng)建一個類,代碼如下:

public?class?MyClass?{
?public?int?compare(Integer?o1,?Integer?o2)?{
?return?o1.compareTo(o2);
?}
}

當我們創(chuàng)建一個該類的對象,并在 Lambda 表達式中使用該對象的方法時,一般可以這么寫:

MyClass?myClass?=?new?MyClass();
Collections.sort(list,?(o1,?o2)?->?myClass.compare(o1,?o2));

注意這里函數(shù)的參數(shù)也是一一對應的,那么采用方法引用的方式,可以這樣簡寫:

MyClass?myClass?=?new?MyClass();
Collections.sort(list,?myClass::compare);

此外,當我們要執(zhí)行的表達式是調(diào)用 Lambda 表達式所在的類的方法時,我們還可以采用如下格式:

this::方法名

例如我在 Lambda 表達式所在的類添加如下方法:

private?int?compare(Integer?o1,?Integer?o2)?{
?return?o1.compareTo(o2);
}

當 Lambda 表達式使用這個方法時,一般可以這樣寫:

Collections.sort(list,?(o1,?o2)?->?compare(o1,?o2));

如果采用方法引用的方式,就可以簡寫成這樣:

Collections.sort(list,?this::compare);

3.3 引用類的方法

引用類的方法所采用的參數(shù)對應形式與上兩種略有不同。如果 Lambda 表達式的“->”的右邊要執(zhí)行的表達式是調(diào)用的“->”的左邊第一個參數(shù)的某個實例方法,并且從第二個參數(shù)開始(或無參)對應到該實例方法的參數(shù)列表時,就可以使用這種方法。焦作國醫(yī)胃腸醫(yī)院地址:http://jz.lieju.com/zhuankeyiyuan/37324455.htm

可能有點繞,假如我們的 Lambda 表達式符合如下格式:

(變量1[,?變量2,?...])?->?變量1.實例方法([變量2,?...])

那么我們的代碼就可以簡寫成:

變量1對應的類名::實例方法名

還是使用 2.3 節(jié)的例子, 當我們使用的 Lambda 表達式是這樣時:

Collections.sort(list,?(o1,?o2)?->?o1.compareTo(o2));

按照上面的說法,就可以簡寫成這樣:

Collections.sort(list,?Integer::compareTo);

3.4 引用構(gòu)造方法

當我們要執(zhí)行的表達式是新建一個對象,并且這個對象的構(gòu)造方法的參數(shù)列表和接口里函數(shù)的參數(shù)列表一一對應時,我們就可以采用「引用構(gòu)造方法」的格式。

假如我們的 Lambda 表達式符合如下格式:

([變量1,?變量2,?...])?->?new?類名([變量1,?變量2,?...])

我們就可以簡寫成如下格式:

類名::new

下面舉個例子說明一下。Java 8 引入了一個 Function 接口,它是一個函數(shù)接口,部分代碼如下:

@FunctionalInterfacepublic?interface?Function{
?/**
?*?Applies?this?function?to?the?given?argument.
?*
?*?@param?t?the?function?argument
?*?@return?the?function?result
?*/
?R?apply(T?t);
?//?省略部分代碼
}

我們用這個接口來實現(xiàn)一個功能,創(chuàng)建一個指定大小的 ArrayList。一般我們可以這樣實現(xiàn):

Functionfunction?=?new?Function()?{
?@Override
?public?ArrayList?apply(Integer?n)?{
?return?new?ArrayList(n);
?}
};
List?list?=?function.apply(10);

使用 Lambda 表達式,我們一般可以這樣寫:

Functionfunction?=?n?->?new?ArrayList(n);

使用「引用構(gòu)造方法」的方式,我們可以簡寫成這樣:

Functionfunction?=?ArrayList::new;

四. 自定義函數(shù)接口

自定義函數(shù)接口很容易,只需要編寫一個只有一個抽象方法的接口即可,示例代碼:

@FunctionalInterfacepublic?interface?MyInterface{
?void?function(T?t);
}

上面代碼中的 @FunctionalInterface 是可選的,但加上該注解編譯器會幫你檢查接口是否符合函數(shù)接口規(guī)范。就像加入 @Override 注解會檢查是否重寫了函數(shù)一樣。

五. 實現(xiàn)原理

經(jīng)過上面的介紹,我們看到 Lambda 表達式只是為了簡化匿名內(nèi)部類書寫,看起來似乎在編譯階段把所有的 Lambda 表達式替換成匿名內(nèi)部類就可以了。但實際情況并非如此,在 JVM 層面,Lambda 表達式和匿名內(nèi)部類其實有著明顯的差別。

5.1 匿名內(nèi)部類的實現(xiàn)

匿名內(nèi)部類仍然是一個類,只是不需要我們顯式指定類名,編譯器會自動為該類取名。比如有如下形式的代碼:

public?class?LambdaTest?{
?public?static?void?main(String[]?args)?{
?new?Thread(new?Runnable()?{
?@Override
?public?void?run()?{
?System.out.println("Hello?World");
?}
?}).start();
?}
}

編譯之后將會產(chǎn)生兩個 class 文件:

LambdaTest.class
LambdaTest$1.class

使用 javap -c LambdaTest.class 進一步分析 LambdaTest.class 的字節(jié)碼,部分結(jié)果如下:

public?static?void?main(java.lang.String[]);?Code:?0:?new?#2?//?class?java/lang/Thread?3:?dup?4:?new?#3?//?class?com/example/myapplication/lambda/LambdaTest$1?7:?dup?8:?invokespecial?#4?//?Method?com/example/myapplication/lambda/LambdaTest$1."":()V?11:?invokespecial?#5?//?Method?java/lang/Thread."":(Ljava/lang/Runnable;)V
?14:?invokevirtual?#6?//?Method?java/lang/Thread.start:()V
?17:?return

可以發(fā)現(xiàn)在 4: new #3 這一行創(chuàng)建了匿名內(nèi)部類的對象。

5.2 Lambda 表達式的實現(xiàn)

接下來我們將上面的示例代碼使用 Lambda 表達式實現(xiàn),代碼如下:

public?class?LambdaTest?{
?public?static?void?main(String[]?args)?{
?new?Thread(()?->?System.out.println("Hello?World")).start();
?}
}

此時編譯后只會產(chǎn)生一個文件 LambdaTest.class,再來看看通過 javap 對該文件反編譯后的結(jié)果:

public?static?void?main(java.lang.String[]);?Code:?0:?new?#2?//?class?java/lang/Thread?3:?dup?4:?invokedynamic?#3,?0?//?InvokeDynamic?#0:run:()Ljava/lang/Runnable;?9:?invokespecial?#4?//?Method?java/lang/Thread."":(Ljava/lang/Runnable;)V
?12:?invokevirtual?#5?//?Method?java/lang/Thread.start:()V
?15:?return

從上面的結(jié)果我們發(fā)現(xiàn) Lambda 表達式被封裝成了主類的一個私有方法,并通過 invokedynamic 指令進行調(diào)用。

因此,我們可以得出結(jié)論:Lambda 表達式是通過 invokedynamic 指令實現(xiàn)的,并且書寫 Lambda 表達式不會產(chǎn)生新的類。

既然 Lambda 表達式不會創(chuàng)建匿名內(nèi)部類,那么在 Lambda 表達式中使用 this 關(guān)鍵字時,其指向的是外部類的引用。

六. 優(yōu)缺點

優(yōu)點:

可以減少代碼的書寫,減少匿名內(nèi)部類的創(chuàng)建,節(jié)省內(nèi)存占用。

使用時不用去記憶所使用的接口和抽象函數(shù)。

缺點:

易讀性較差,閱讀代碼的人需要熟悉 Lambda 表達式和抽象函數(shù)中參數(shù)的類型。


文章標題:Java8:一文帶你掌握Lambda表達式
文章鏈接:http://weahome.cn/article/jdidoj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部