java8中的lambda表達(dá)式簡(jiǎn)介
Lambda表達(dá)式類(lèi)似匿名函數(shù),簡(jiǎn)單地說(shuō),它是沒(méi)有聲明的方法,也即沒(méi)有訪(fǎng)問(wèn)修飾符、返回值聲明和方法名。
Lambda允許把函數(shù)作為一個(gè)方法的參數(shù)(函數(shù)作為參數(shù)傳遞進(jìn)方法中)。
Lambda表達(dá)式的語(yǔ)法
(parameters) -> expression
或
(parameters) -> { statements; }參數(shù)說(shuō)明:
- 可選類(lèi)型聲明:不需要聲明參數(shù)類(lèi)型,編譯器可以統(tǒng)一識(shí)別參數(shù)值。
- 可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無(wú)需定義圓括號(hào),但多個(gè)參數(shù)需要定義圓括號(hào)。
- 可選的大括號(hào):如果主體包含了一個(gè)語(yǔ)句,就不需要使用大括號(hào)。
- 可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值則編譯器會(huì)自動(dòng)返回值,大括號(hào)需要指明表達(dá)式返回了一個(gè)數(shù)值。
舉例說(shuō)明:
// 1. 不需要參數(shù),返回值為5 () -> 5 // 2. 接收一個(gè)參數(shù)(數(shù)字類(lèi)型),返回其2倍的值 x -> 2 * x // 3. 接受2個(gè)參數(shù)(數(shù)字),并返回他們的差值 (x, y) -> x – y // 4. 接收2個(gè)int型整數(shù),返回他們的和 (int x, int y) -> x + y // 5. 接受一個(gè) string 對(duì)象,并在控制臺(tái)打印,不返回任何值(看起來(lái)像是返回void) (String s) -> System.out.print(s)
Lambda表達(dá)式作用域
lambda表達(dá)式中可以引用任何外部的變量或者常量。但是對(duì)這些外部的變量是有要求的:它們必須是Effectively final的。
局部?jī)?nèi)部類(lèi)和匿名內(nèi)部類(lèi)訪(fǎng)問(wèn)的局部變量必須由final修飾,java8開(kāi)始,可以不加final修飾符,由系統(tǒng)默認(rèn)添加。java將這個(gè)功能稱(chēng)為:Effectively final功能。
方法引用
指向靜態(tài)方法的方法引用
Function<String, Integer> function1 = Integer::parseInt; // 等價(jià)于下面 Function<String, Integer> function2 = (String i) -> Integer.parseInt(i);
指向任意類(lèi)型實(shí)例方法的方法引用
Function<String, String> function3 = String::toLowerCase; // 等價(jià)于下面 Function<String, String> function4 = (String i) -> i.toLowerCase(); BiFunction<String, Integer, String> biFunction = (String s, Integer i) -> s.substring(i); BiFunction<String, Integer, String> biFunction2 = String::substring;
指向現(xiàn)有對(duì)象的實(shí)例方法的方法引用
String str = "hello"; Supplier<Integer> supplier = () -> str.length(); Supplier<Integer> supplier2 = str::length; Function<Integer, String> function5 = (Integer i) -> str.substring(i); Function<Integer, String> function6 = str::substring;
構(gòu)造方法引用
package com.morris.java8.lamdba;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodReferenceExample {
public static void main(String[] args) {
// 構(gòu)造函數(shù)引用
Supplier<String> stringSupplier = () -> new String();
Supplier<String> stringSupplier2 = String::new;
Function<String, String> stringFunction = (String s)->new String(s);
Function<String, String> stringFunction2 = String::new;
BiFunction<Runnable, String, Thread> stringBiFunction = (Runnable r, String b)-> new Thread(r, b);
BiFunction<Runnable, String, Thread> stringBiFunction2 = Thread::new;
ThreeFunction<ThreadGroup, Runnable, String, Thread> threeFunction = (ThreadGroup g, Runnable r, String b)-> new Thread(g, r, b);
ThreeFunction<ThreadGroup, Runnable, String, Thread> threeFunction2 = Thread::new;
}
interface ThreeFunction<A, B, C, D> {
D triple(A a, B b, C c);
}
}
lambda與匿名內(nèi)部類(lèi)
從表面上看到Lambda表達(dá)式似乎只是為了簡(jiǎn)化匿名內(nèi)部類(lèi)書(shū)寫(xiě),這看起來(lái)僅僅通過(guò)語(yǔ)法糖在編譯階段把所有的Lambda表達(dá)式替換成匿名內(nèi)部類(lèi)就可以了。但實(shí)際并非如此。在JVM層面,Lambda表達(dá)式和匿名內(nèi)部類(lèi)有著明顯的差別。
匿名內(nèi)部類(lèi)
匿名內(nèi)部類(lèi)仍然是一個(gè)類(lèi),只是不需要程序員顯示指定類(lèi)名,編譯器會(huì)自動(dòng)為該類(lèi)取名。
public class AnonymousClassDemo {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("this is an Anonymous class demo");
}
});
}
}
因此上面的代碼,編譯之后將會(huì)產(chǎn)生兩個(gè)class文件:
AnonymousClassDemo.class AnonymousClassDemo$1.class
進(jìn)一步分析主類(lèi)AnonymousClassDemo.class的字節(jié)碼,可發(fā)現(xiàn)其創(chuàng)建了匿名內(nèi)部類(lèi)的對(duì)象:
$ javap -v -p AnonymousClassDemo.class
...
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
0: new #2 // class java/lang/Thread
3: dup
4: new #3 // class AnonymousClassDemo$1 創(chuàng)建匿名內(nèi)部類(lèi)
7: dup
8: invokespecial #4 // Method AnonymousClassDemo$1."<init>":()V
11: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
14: pop
15: return
LineNumberTable:
line 5: 0
line 11: 15
}
SourceFile: "AnonymousClassDemo.java"
InnerClasses:
static #3; //class AnonymousClassDemo$1lambda表達(dá)式
Lambda表達(dá)式通過(guò)invokedynamic指令實(shí)現(xiàn),不會(huì)產(chǎn)生新的類(lèi)。
public class LambdaDemo {
public static void main(String[] args) {
new Thread(()-> System.out.println("this is a lambda demo"));
}
}上面的代碼編譯之后只有一個(gè)class文件:
LambdaDemo.class
通過(guò)javap查看LambdaDemo.class的字節(jié)碼,我們更能看出Lambda表達(dá)式內(nèi)部表示的不同。
$ javap -v -p LambdaDemo.class
...
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
0: new #2 // class java/lang/Thread
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 使用invokedynamic指令調(diào)用
9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
12: pop
13: return
LineNumberTable:
line 4: 0
line 5: 13
private static void lambda$main$0(); // Lambda表達(dá)式被封裝成主類(lèi)的私有方法
descriptor: ()V
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #6 // String this is a lambda demo
5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 4: 0
}
SourceFile: "LambdaDemo.java"
InnerClasses:
public static final #51= #50 of #54; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #22 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#23 ()V
#24 invokestatic LambdaDemo.lambda$main$0:()V
#23 ()V
反編譯之后我們發(fā)現(xiàn)Lambda表達(dá)式被封裝成了主類(lèi)的一個(gè)私有方法,并通過(guò)invokedynamic指令進(jìn)行調(diào)用。
既然Lambda表達(dá)式不是內(nèi)部類(lèi)的簡(jiǎn)寫(xiě),那么Lambda內(nèi)部的this引用也就跟內(nèi)部類(lèi)對(duì)象沒(méi)什么關(guān)系了。在Lambda表達(dá)式中this的意義跟在表達(dá)式外部完全一樣。
總結(jié)
相同點(diǎn):
- 都可以直接訪(fǎng)問(wèn)“effectively final”的局部變量(不需要加final關(guān)鍵字,但實(shí)際上是final,編譯器編譯時(shí)會(huì)自動(dòng)加上),以及外部類(lèi)的成員變量(包括實(shí)例變量和類(lèi)變量)。
不同點(diǎn):
- 匿名內(nèi)部類(lèi)可以為任意接口創(chuàng)建實(shí)例——不管接口包含多少個(gè)抽象方法,只要匿名內(nèi)部類(lèi)實(shí)現(xiàn)所有的抽象方法即可。但Lambda表達(dá)式只能為函數(shù)式接口創(chuàng)建實(shí)例。
- 匿名內(nèi)部類(lèi)可以為抽象類(lèi)、甚至普通類(lèi)創(chuàng)建實(shí)例,但Lambda表達(dá)式只能為函數(shù)式接口創(chuàng)建實(shí)例。
- 匿名內(nèi)部類(lèi)實(shí)現(xiàn)的抽象方法的方法體允許調(diào)用接口中定義的默認(rèn)方法;但Lambda表達(dá)式的代碼塊不允許調(diào)用接口中定義的默認(rèn)方法。
到此這篇關(guān)于java8 lambda表達(dá)式簡(jiǎn)介的文章就介紹到這了,更多相關(guān)java8 lambda表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 一文帶你看懂Java8中的lambda表達(dá)式和方法引用
- Java8新特性-Lambda表達(dá)式詳解
- java8新特性-lambda表達(dá)式入門(mén)學(xué)習(xí)心得
- Java8?lambda表達(dá)式的10個(gè)實(shí)例講解
- 詳解Java8如何使用Lambda表達(dá)式進(jìn)行比較
- Java8新特性:lambda表達(dá)式總結(jié)
- java8 forEach結(jié)合Lambda表達(dá)式遍歷 List操作
- Java8 用Lambda表達(dá)式給List集合排序的實(shí)現(xiàn)
- Java8中的Lambda表達(dá)式理解與使用示例詳解
相關(guān)文章
Spring中利用IOC實(shí)現(xiàn)注入的方式
Spring IOC(控制反轉(zhuǎn))實(shí)現(xiàn)依賴(lài)注入,將對(duì)象創(chuàng)建和依賴(lài)關(guān)系的管理交由Spring容器處理,通過(guò)注解或XML配置,實(shí)現(xiàn)對(duì)象之間的松耦合,提高代碼復(fù)用性和可維護(hù)性2023-04-04
Spring Cloud Gateway 獲取請(qǐng)求體(Request Body)的多種方法
這篇文章主要介紹了Spring Cloud Gateway 獲取請(qǐng)求體(Request Body)的多種方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
java日期時(shí)間格式化@JsonFormat與@DateTimeFormat的使用
本文主要介紹了java日期時(shí)間格式化@JsonFormat與@DateTimeFormat的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
IDEA快速搭建Java開(kāi)發(fā)環(huán)境的教程圖解
這篇文章主要介紹了IDEA如何快速搭建Java開(kāi)發(fā)環(huán)境,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11
在Java的MyBatis框架中建立接口進(jìn)行CRUD操作的方法
這篇文章主要介紹了在Java的MyBatis框架中建立接口進(jìn)行CRUD操作的方法,CRUD是指在做計(jì)算處理時(shí)的增加(Create)、重新取得數(shù)據(jù)(Retrieve)、更新(Update)和刪除(Delete)幾個(gè)單詞的首字母簡(jiǎn)寫(xiě),需要的朋友可以參考下2016-04-04
java數(shù)組及arrays類(lèi)對(duì)數(shù)組的操作實(shí)例
下面小編就為大家?guī)?lái)一篇java數(shù)組及arrays類(lèi)對(duì)數(shù)組的操作實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
使用java采集京東商城區(qū)劃數(shù)據(jù)示例
這篇文章主要介紹了java采集京東的全國(guó)區(qū)劃數(shù)據(jù)示例,保存成json形式,如想轉(zhuǎn)換到數(shù)據(jù)庫(kù)只需反序列化為對(duì)象保存到數(shù)據(jù)庫(kù)即可2014-03-03
java開(kāi)源項(xiàng)目jeecgboot的超詳細(xì)解析
JeecgBoot是一款基于BPM的低代碼平臺(tái),下面這篇文章主要給大家介紹了關(guān)于java開(kāi)源項(xiàng)目jeecgboot的相關(guān)資料,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10

