日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Lambda表达式超详细总结

發(fā)布時間:2024/3/12 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Lambda表达式超详细总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

    • 1. 什么是Lambda表達(dá)式
    • 2. 為什么使用Lambda表達(dá)式
    • 3. Lambda表達(dá)式語法
    • 4. 函數(shù)式接口
      • 4.1 什么是函數(shù)式接口
      • 4.2 自定義函數(shù)式接口
      • 4.3 Java內(nèi)置函數(shù)式接口
    • 5. 方法引用
    • 6. 構(gòu)造器引用
    • 7. 數(shù)組引用
    • 8. Lambda表達(dá)式的作用域
      • 8.1 訪問局部變量
      • 8.2 訪問局部引用,靜態(tài)變量,實例變量
      • 8.3 Lambda表達(dá)式訪問局部變量作限制的原因
    • 9. Lambda表達(dá)式的優(yōu)缺點

1. 什么是Lambda表達(dá)式

Lambda表達(dá)式,也可稱為閉包。類似于JavaScript中的閉包,它是推動Java8發(fā)布的最重要的新特性。

2. 為什么使用Lambda表達(dá)式

我們可以把Lambda表達(dá)式理解為一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣進(jìn)行傳遞)。Lambda允許把函數(shù)作為一個方法的參數(shù),使用Lambda表達(dá)式可以寫出更簡潔、更靈活的代碼,而其作為一種更緊湊的代碼風(fēng)格,使Java的語言表達(dá)能力得到了提升。

一個簡單示例:

分別使用成員內(nèi)部類、局部內(nèi)部類、靜態(tài)內(nèi)部類、匿名內(nèi)部類方式實現(xiàn)Runnable的run()方法并創(chuàng)建和啟動線程,如下所示:

public class LambdaDemo {/*** 成員內(nèi)部類*/class MyThread01 implements Runnable{@Overridepublic void run() {System.out.println("成員內(nèi)部類:用Lambda語法創(chuàng)建線程吧!");}}/*** 靜態(tài)內(nèi)部類*/static class MyThread02 implements Runnable{@Overridepublic void run() {System.out.println("靜態(tài)內(nèi)部類:對啊,用Lambda語法創(chuàng)建線程吧!");}}public static void main(String[] args) {/*** 局部內(nèi)部類*/class MyThread03 implements Runnable{@Overridepublic void run() {System.out.println("局部內(nèi)部類:用Lambda語法創(chuàng)建線程吧!");}}/*** 匿名內(nèi)部類*/Runnable runnable = new Runnable(){@Overridepublic void run() {System.out.println("匿名內(nèi)部類:求求你,用Lambda語法創(chuàng)建線程吧!");}};//成員內(nèi)部類方式LambdaDemo lambdaDemo = new LambdaDemo();MyThread01 myThread01 =lambdaDemo.new MyThread01();new Thread(myThread01).start();//靜態(tài)內(nèi)部類方式MyThread02 myThread02 = new MyThread02();new Thread(myThread02).start();//局部內(nèi)部類MyThread03 myThread03 = new MyThread03();new Thread(myThread03).start();//匿名內(nèi)部類的方式new Thread(runnable).start();} }

執(zhí)行結(jié)果:

可以看到上面創(chuàng)建方式,代碼量都不少,使用Lambda表達(dá)式實現(xiàn),如下所示:

//Lambda方式 new Thread(() -> System.out.println("使用Lambda就對了")).start();

可以看到代碼明顯簡潔了許多。

3. Lambda表達(dá)式語法

Lambda表達(dá)式在Java語言中引入了一個操作符**“->”**,該操作符被稱為Lambda操作符或箭頭操作符。它將Lambda分為兩個部分:

  • 左側(cè):指定了Lambda表達(dá)式需要的所有參數(shù)
  • 右側(cè):制定了Lambda體,即Lambda表達(dá)式要執(zhí)行的功能。

像這樣:

(parameters) -> expression 或 (parameters) ->{ statements; }

以下是lambda表達(dá)式的重要特征:

  • 可選類型聲明:不需要聲明參數(shù)類型,編譯器可以統(tǒng)一識別參數(shù)值。
  • 可選的參數(shù)圓括號:一個參數(shù)無需定義圓括號,但無參數(shù)或多個參數(shù)需要定義圓括號。
  • 可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
  • 可選的返回關(guān)鍵字:如果主體只有一個表達(dá)式返回值則編譯器會自動返回值,大括號需要指定明表達(dá)式返回了一個數(shù)值。

下面對每個語法格式的特征進(jìn)行舉例說明:

(1)語法格式一:無參,無返回值,Lambda體只需一條語句。如下:

@Testpublic void test01(){Runnable runnable=()-> System.out.println("Runnable 運行");runnable.run();//結(jié)果:Runnable 運行}

(2)語法格式二:Lambda需要一個參數(shù),無返回值。如下:

@Testpublic void test02(){Consumer<String> consumer=(x)-> System.out.println(x);consumer.accept("Hello Consumer");//結(jié)果:Hello Consumer}

(3)語法格式三:Lambda只需要一個參數(shù)時,參數(shù)的小括號可以省略,如下:

public void test02(){Consumer<String> consumer=x-> System.out.println(x);consumer.accept("Hello Consumer");//結(jié)果:Hello Consumer}

(4)語法格式四:Lambda需要兩個參數(shù),并且Lambda體中有多條語句。

@Testpublic void test04(){Comparator<Integer> com=(x, y)->{System.out.println("函數(shù)式接口");return Integer.compare(x,y);};System.out.println(com.compare(2,4));//結(jié)果:-1}

(5)語法格式五:有兩個以上參數(shù),有返回值,若Lambda體中只有一條語句,return和大括號都可以省略不寫

@Testpublic void test05(){Comparator<Integer> com=(x, y)-> Integer.compare(x,y);System.out.println(com.compare(4,2));//結(jié)果:1}

(6)Lambda表達(dá)式的參數(shù)列表的數(shù)據(jù)類型可以省略不寫,因為JVM可以通過上下文推斷出數(shù)據(jù)類型,即“類型推斷”

@Testpublic void test06(){Comparator<Integer> com=(Integer x, Integer y)-> Integer.compare(x,y);System.out.println(com.compare(4,2));//結(jié)果:1}

類型推斷:在執(zhí)行javac編譯程序時,JVM根據(jù)程序的上下文推斷出了參數(shù)的類型。Lambda表達(dá)式依賴于上下文環(huán)境。

語法背誦口訣:左右遇一括號省,左側(cè)推斷類型省,能省則省。

4. 函數(shù)式接口

4.1 什么是函數(shù)式接口

==只包含一個抽象方法的接口,就稱為函數(shù)式接口。==我們可以通過Lambda表達(dá)式來創(chuàng)建該接口的實現(xiàn)對象。
我們可以在任意函數(shù)式接口上使用@FunctionalInterface注解,這樣做可以用于檢測它是否是一個函數(shù)式接口,同時javadoc也會包含一條聲明,說明這個接口是一個函數(shù)式接口。

4.2 自定義函數(shù)式接口

按照函數(shù)式接口的定義,自定義一個函數(shù)式接口,如下:

@FunctionalInterface public interface MyFuncInterf<T> {public T getValue(String origin); }

定義一個方法將函數(shù)式接口作為方法參數(shù)。

public String toLowerString(MyFuncInterf<String> mf,String origin){return mf.getValue(origin);}

將Lambda表達(dá)式實現(xiàn)的接口作為參數(shù)傳遞。

public void test07(){String value=toLowerString((str)->{return str.toLowerCase();},"ABC");System.out.println(value);//結(jié)果ABC}

4.3 Java內(nèi)置函數(shù)式接口

四大核心函數(shù)式接口的介紹,如圖所示:

使用示例:
1.Consumer:消費型接口 void accept(T t)

public void makeMoney(Integer money, Consumer<Integer> consumer){consumer.accept(money);}@Testpublic void test01(){makeMoney(100,t-> System.out.println("今天賺了"+t));//結(jié)果:今天賺了100}

2.Supplier:供給型接口 T get()

/*** 產(chǎn)生指定的整數(shù)集合放到集合中* Iterable接口的forEach方法的定義:方法中使用到了Consumer消費型接口,* default void forEach(Consumer<? super T> action) {* Objects.requireNonNull(action);* for (T t : this) {* action.accept(t);* }* }*/@Testpublic void test02(){List list = addNumInList(10, () -> (int) (Math.random() * 100));list.forEach(t-> System.out.println(t));}public List addNumInList(int size, Supplier<Integer> supplier){List<Integer> list=new ArrayList();for (int i = 0; i < size; i++) {list.add(supplier.get());}return list;}

3.Function<T,R>:函數(shù)型接口 R apply(T t)

/*** * 使用函數(shù)式接口處理字符串。*/public String handleStr(String s,Function<String,String> f){return f.apply(s);}@Testpublic void test03(){System.out.println(handleStr("abc",(String s)->s.toUpperCase()));}//結(jié)果:ABC

4.Predicate:斷言型接口 boolean test(T t)

/*** 自定義條件過濾字符串集合*/@Testpublic void test04(){List<String> strings = Arrays.asList("啊啊啊", "2333", "666", "?????????");List<String> stringList = filterStr(strings, (s) -> s.length() > 3);for (String s : stringList) {System.out.println(s);}}public List<String> filterStr(List<String> list, Predicate<String> predicate){ArrayList result = new ArrayList();for (int i = 0; i < list.size(); i++) {if (predicate.test(list.get(i))){result.add(list.get(i));}}return result;}

其他接口的定義,如圖所示:

5. 方法引用

當(dāng)要傳遞給Lambda體的操作,已經(jīng)有實現(xiàn)的方法了,就可以使用方法引用!(實現(xiàn)抽象方法的參數(shù)列表,必須與方法引用的參數(shù)列表一致,方法的返回值也必須一致,即方法的簽名一致)。方法引用可以理解為方法引用是Lambda表達(dá)式的另外一種表現(xiàn)形式。
方法引用的語法:使用操作符“::”將對象或類和方法名分隔開。
方法引用的使用情況共分為以下三種:

  • 對象::實例方法名
  • 類::靜態(tài)方法名
  • 類::實例方法名

使用示例:
1.對象::實例方法名

/***PrintStream中的println方法定義 * public void println(String x) {* synchronized (this) {* print(x);* newLine();* }* }*///對象::實例方法名@Testpublic void test1(){PrintStream out = System.out;Consumer<String> consumer=out::println;consumer.accept("hello");}
  • 類::靜態(tài)方法名
  • /*** Integer類中的靜態(tài)方法compare的定義:* public static int compare(int x, int y) {* return (x < y) ? -1 : ((x == y) ? 0 : 1);* }*/@Testpublic void test2(){Comparator<Integer> comparable=(x,y)->Integer.compare(x,y);//使用方法引用實現(xiàn)相同效果Comparator<Integer> integerComparable=Integer::compare;System.out.println(integerComparable.compare(4,2));//結(jié)果:1System.out.println(comparable.compare(4,2));//結(jié)果:1}

    3.類::實例方法名

    @Testpublic void test3(){BiPredicate<String,String> bp=(x,y)->x.equals(y);//使用方法引用實現(xiàn)相同效果BiPredicate<String,String> bp2=String::equals;System.out.println(bp.test("1","2"));//結(jié)果:falseSystem.out.println(bp.test("1","2"));//結(jié)果:false}

    6. 構(gòu)造器引用

    格式:類名::new
    與函數(shù)式接口相結(jié)合,自動與函數(shù)式接口中方法兼容,可以把構(gòu)造器引用賦值給定義的方法。需要注意構(gòu)造器參數(shù)列表要與接口中抽象方法的參數(shù)列表一致。使用示例:
    創(chuàng)建一個實體類Employee:

    public class Employee {private Integer id;private String name;private Integer age;@Overridepublic String toString() {return "Employee{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}public Employee(){}public Employee(Integer id) {this.id = id;}public Employee(Integer id, Integer age) {this.id = id;this.age = age;}public Employee(int id, String name, int age) {this.id = id;this.name = name;this.age = age;} }

    使用構(gòu)造器引用與函數(shù)式接口相結(jié)合

    @Testpublic void test01(){//引用無參構(gòu)造器Supplier<Employee> supplier=Employee::new;System.out.println(supplier.get());//引用有參構(gòu)造器Function<Integer,Employee> function=Employee::new;System.out.println(function.apply(21));BiFunction<Integer,Integer,Employee> biFunction=Employee::new;System.out.println(biFunction.apply(8,24));}輸出結(jié)果:Employee{id=null, name='null', age=null}Employee{id=21, name='null', age=null}Employee{id=8, name='null', age=24}

    7. 數(shù)組引用

    數(shù)組引用的格式:type[]:new
    使用示例:

    @Testpublic void test02(){Function<Integer,String[]> function=String[]::new;String[] apply = function.apply(10);System.out.println(apply.length);//結(jié)果:10}

    8. Lambda表達(dá)式的作用域

    Lambda表達(dá)式可以看作是匿名內(nèi)部類實例化的對象,Lambda表達(dá)式對變量的訪問限制和匿名內(nèi)部類一樣,因此Lambda表達(dá)式可以訪問局部變量、局部引用,靜態(tài)變量,實例變量。

    8.1 訪問局部變量

    在Lambda表達(dá)式中規(guī)定只能引用標(biāo)記了final的外層局部變量。我們不能在lambda 內(nèi)部修改定義在域外的局部變量,否則會編譯錯誤。

    public class TestFinalVariable {interface VarTestInterface{Integer change(String str);}public static void main(String[] args) {//局部變量不使用final修飾Integer tempInt = 1;VarTestInterface var = (str -> Integer.valueOf(str+tempInt));//再次修改,不符合隱式final定義tempInt =2;Integer str =var.change("111") ;System.out.println(str);} }

    上面代碼會出現(xiàn)編譯錯誤,出現(xiàn)如下提示:

    特殊情況下,局部變量也可以不用聲明為 final,但是必須不可被后面的代碼修改(即隱性的具有 final 的語義)

    例如上面的代碼確保Lambda表達(dá)式后局部變量后面不做修改,就可以成功啦!

    public class TestFinalVariable {interface VarTestInterface{Integer change(String str);}public static void main(String[] args) {//局部變量不使用final修飾Integer tempInt = 1;VarTestInterface var = (str -> Integer.valueOf(str+tempInt));Integer str =var.change("111") ;System.out.println(str);} }

    8.2 訪問局部引用,靜態(tài)變量,實例變量

    Lambda表達(dá)式不限制訪問局部引用變量,靜態(tài)變量,實例變量。代碼測試都可正常執(zhí)行,代碼:

    public class LambdaScopeTest {/*** 靜態(tài)變量*/private static String staticVar;/*** 實例變量*/private static String instanceVar;@FunctionalInterfaceinterface VarChangeInterface{Integer change(String str);}/*** 測試引用變量*/private void testReferenceVar(){ArrayList<String> list = new ArrayList<>();list.add("111");//訪問外部引用局部引用變量VarChangeInterface varChangeInterface = ((str) -> Integer.valueOf(list.get(0)));//修改局部引用變量list.set(0,"222");Integer str =varChangeInterface.change("");System.out.println(str);}/*** 測試靜態(tài)變量*/void testStaticVar(){staticVar="222";VarChangeInterface varChangeInterface = (str -> Integer.valueOf(str+staticVar));staticVar="333";Integer str =varChangeInterface.change("111") ;System.out.println(str);}/*** 測試實例變量*/void testInstanceVar(){instanceVar="222";VarChangeInterface varChangeInterface = (str -> Integer.valueOf(str+instanceVar));instanceVar="333";Integer str =varChangeInterface.change("111") ;System.out.println(str);}public static void main(String[] args) {new LambdaScopeTest().testReferenceVar();new LambdaScopeTest().testStaticVar();new LambdaScopeTest().testInstanceVar();} }

    Lambda表達(dá)式里不允許聲明一個與局部變量同名的參數(shù)或者局部變量。

    //編程報錯Integer tempInt = 1;VarTestInterface varTest01 = (tempInt -> Integer.valueOf(tempInt));VarTestInterface varTest02 = (str -> {Integer tempInt = 1;Integer.valueOf(str);});

    8.3 Lambda表達(dá)式訪問局部變量作限制的原因

    Lambda表達(dá)式不能訪問非final修飾的局部變量的原因是,局部變量是保存在棧幀中的。而在Java的線程模型中,棧幀中的局部變量是線程私有的,如果允許Lambda表達(dá)式訪問到棧幀中的變量地址(可改變的局部變量),則會可能導(dǎo)致線程私有的數(shù)據(jù)被并發(fā)訪問,造成線程不安全問題。

    基于上述,對于引用類型的局部變量,因為Java是值傳遞,又因為引用類型的指向內(nèi)容是保存在堆中,是線程共享的,因此Lambda表達(dá)式中可以修改引用類型的局部變量的內(nèi)容,而不能修改該變量的引用。

    對于基本數(shù)據(jù)類型的變量,在 Lambda表達(dá)式中只是獲取到該變量的副本,且局部變量是線程私有的,因此無法知道其他線程對該變量的修改,如果該變量不做final修飾,會造成數(shù)據(jù)不同步的問題。

    但是實例變量,靜態(tài)變量不作限制,因為實例變量,靜態(tài)變量是保存在堆中(Java8之后),而堆是線程共享的。在Lambda表達(dá)式內(nèi)部是可以知道實例變量,靜態(tài)變量的變化。

    9. Lambda表達(dá)式的優(yōu)缺點

    • 優(yōu)點:
  • 使代碼更簡潔,緊湊
  • 可以使用并行流來并行處理,充分利用多核CPU的優(yōu)勢
    有利于JIT編譯器對代碼進(jìn)行優(yōu)化
    • 缺點:
  • 非并行計算情況下,其計算速度沒有比傳統(tǒng)的 for 循環(huán)快
  • 不容易調(diào)試
  • 若其他程序員沒有學(xué)過 Lambda 表達(dá)式,代碼不容易看懂
  • 在Stream操作中使用lambda表達(dá)式:Java8新特性Stream的使用總結(jié)

    筆記總結(jié)自:尚硅谷的視頻教程-【Java8新特性】
    參考:
    1.Java 8 Lambda 表達(dá)式
    2.Lambda-讓人又愛又恨的“->"
    推薦閱讀:聊聊Java 8 Lambda 表達(dá)式

    總結(jié)

    以上是生活随笔為你收集整理的Lambda表达式超详细总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。