JavaSE中Lambda表達式的使用與變量捕獲
Lambda表達式的背景
Lambda表達式是Java SE 8中一個重要的新特性。lambda表達式允許你通過表達式來代替功能接口。 lambda表達式就和方法一樣,它提供了一個正常的參數(shù)列表和一個使用這些參數(shù)的主體(body,可以是一個表達式或一個代碼塊)。 Lambda 表達式(Lambda expression),基于數(shù)學中的λ演算得名,也可稱為閉包(Closure) 。
Lambda表達式的語法
基本語法: (parameters) -> expression 或 (parameters) ->{ statements; }
Lambda表達式由三部分組成:
paramaters:類似方法中的形參列表,這里的參數(shù)是函數(shù)式接口里的參數(shù)。這里的參數(shù)類型可以明確的聲明也可不聲明而由JVM隱含的推斷。另外當只有一個推斷類型時可以省略掉圓括號。
->:可理解為“被用于”的意思
方法體:可以是表達式也可以代碼塊,是函數(shù)式接口里方法的實現(xiàn)。代碼塊可返回一個值或者什么都不反回,這里的代碼塊塊等同于方法的方法體。如果是表達式,也可以返回一個值或者什么都不反回。
示例如下:
// 1. 不需要參數(shù),返回值為 2 () -> 2 // 2. 接收一個參數(shù)(數(shù)字類型),返回其2倍的值 x -> 2 * x // 3. 接受2個參數(shù)(數(shù)字),并返回他們的和 (x, y) -> x + y // 4. 接收2個int型整數(shù),返回他們的乘積 (int x, int y) -> x * y // 5. 接受一個 string 對象,并在控制臺打印,不返回任何值(看起來像是返回void) (String s) -> System.out.print(s)
函數(shù)式接口
要了解Lambda表達式,首先需要了解什么是函數(shù)式接口,函數(shù)式接口定義:一個接口有且只有一個抽象方法
注意:
- 如果一個接口只有一個抽象方法,那么該接口就是一個函數(shù)式接口
- 如果我們在某個接口上聲明了 @FunctionalInterface 注解,那么編譯器就會按照函數(shù)式接口的定義來要求該接口,這樣如果有兩個抽象方法,程序編譯就會報錯的。所以,從某種意義上來說,只要你保證你的接口中只有一個抽象方法,你可以不加這個注解。加上就會自動進行檢測的。
定義方式:
@FunctionalInterface interface NoParameterNoReturn { //注意:只能有一個方法 void test(); }
也提供了另一種方式:
@FunctionalInterface interface NoParameterNoReturn { void test(); default void test2() { System.out.println("JDK1.8新特性,default默認方法可以有具體的實現(xiàn)"); } }
Lambda表達式的基本使用
首先,我們實現(xiàn)準備好幾個接口:
//無返回值無參數(shù) @FunctionalInterface interface NoParameterNoReturn { void test(); } //無返回值一個參數(shù) @FunctionalInterface interface OneParameterNoReturn { void test(int a); } //無返回值多個參數(shù) @FunctionalInterface interface MoreParameterNoReturn { void test(int a,int b); } //有返回值無參數(shù) @FunctionalInterface interface NoParameterReturn { int test(); } //有返回值一個參數(shù) @FunctionalInterface interface OneParameterReturn { int test(int a); } //有返回值多參數(shù) @FunctionalInterface interface MoreParameterReturn { int test(int a,int b); }
我們在上面提到過,Lambda可以理解為:Lambda就是匿名內(nèi)部類的簡化,實際上是創(chuàng)建了一個類,實現(xiàn)了接口,重寫了接口的方法 。
沒有使用lambda表達式的時候的調(diào)用方式 :
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){ @Override public void test() { System.out.println("hello"); } }; noParameterNoReturn.test();
具體使用見以下示例代碼:
public class TestDemo { public static void main(String[] args) { NoParameterNoReturn noParameterNoReturn = ()->{ System.out.println("無參數(shù)無返回值"); }; noParameterNoReturn.test(); OneParameterNoReturn oneParameterNoReturn = (int a)->{ System.out.println("一個參數(shù)無返回值:"+ a); }; oneParameterNoReturn.test(10); MoreParameterNoReturn moreParameterNoReturn = (int a,int b)->{ System.out.println("多個參數(shù)無返回值:"+a+" "+b); }; moreParameterNoReturn.test(20,30); NoParameterReturn noParameterReturn = ()->{ System.out.println("有返回值無參數(shù)!"); return 40; }; //接收函數(shù)的返回值 int ret = noParameterReturn.test(); System.out.println(ret); OneParameterReturn oneParameterReturn = (int a)->{ System.out.println("有返回值有一個參數(shù)!"); return a; }; ret = oneParameterReturn.test(50); System.out.println(ret); MoreParameterReturn moreParameterReturn = (int a,int b)->{ System.out.println("有返回值多個參數(shù)!"); return a+b; }; ret = moreParameterReturn.test(60,70); System.out.println(ret); } }
語法精簡
- 參數(shù)類型可以省略,如果需要省略,每個參數(shù)的類型都要省略。
- 參數(shù)的小括號里面只有一個參數(shù),那么小括號可以省略
- 如果方法體當中只有一句代碼,那么大括號可以省略
- 如果方法體中只有一條語句,且是return語句,那么大括號可以省略,且去掉return關鍵字。
示例代碼:
public static void main(String[] args) { MoreParameterNoReturn moreParameterNoReturn = ( a, b)->{ System.out.println("無返回值多個參數(shù),省略參數(shù)類型:"+a+" "+b); }; moreParameterNoReturn.test(20,30); OneParameterNoReturn oneParameterNoReturn = a ->{ System.out.println("無參數(shù)一個返回值,小括號可以勝率:"+ a); }; oneParameterNoReturn.test(10); NoParameterNoReturn noParameterNoReturn = ()->System.out.println("無參數(shù)無返回值,方法體中只有一行代碼"); noParameterNoReturn.test(); //方法體中只有一條語句,且是return語句 NoParameterReturn noParameterReturn = ()-> 40; int ret = noParameterReturn.test(); System.out.println(ret); }
變量捕獲
Lambda 表達式中存在變量捕獲 ,了解了變量捕獲之后,我們才能更好的理解Lambda 表達式的作用域 。Java當中的匿名類中,會存在變量捕獲
匿名內(nèi)部類
匿名內(nèi)部類就是沒有名字的內(nèi)部類 。我們這里只是為了說明變量捕獲
匿名內(nèi)部類的簡單的使用如下:
class Test { public void func(){ System.out.println("func()"); } } public class TestDemo { public static void main(String[] args) { new Test(){ @Override public void func() { System.out.println("我是內(nèi)部類,且重寫了func這個方法!"); } }; } }
匿名內(nèi)部類的變量捕獲
class Test { public void func(){ System.out.println("func()"); } } public class TestDemo { public static void main(String[] args) { int a = 100; new Test(){ @Override public void func() { System.out.println("我是內(nèi)部類,且重寫了func這個方法!"); System.out.println("我是捕獲到變量 a == "+a +" 我是一個常量,或者是一個沒有改變過值的變量!"); } }; } }
在上述代碼當中的變量a就是,捕獲的變量。這個變量要么是被final修飾,如果不是被final修飾的 你要保證在使用之前,沒有修改
Lambda的變量捕獲
在Lambda當中也可以進行變量的捕獲,具體我們看一下代碼:
@FunctionalInterface interface NoParameterNoReturn { void test(); } public static void main(String[] args) { int a = 10; NoParameterNoReturn noParameterNoReturn = ()->{ // a = 99; error System.out.println("捕獲變量:"+a); }; noParameterNoReturn.test(); }
Lambda在集合當中的使用
為了能夠讓Lambda和Java的集合類集更好的一起使用,集合當中,也新增了部分接口,以便與Lambda表達式對接
注意:Collection的forEach()方法是從接口 java.lang.Iterable 拿過來的。
Collection接口
forEach() 方法演示
該方法在接口 Iterable 當中,原型如下
default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }
該方法表示:對容器中的每個元素執(zhí)行action指定的動作 。
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("Hello"); list.add("腳本之家"); list.add("hello"); list.add("網(wǎng)站"); list.forEach(new Consumer<String>(){ @Override public void accept(String str){ //簡單遍歷集合中的元素。 System.out.print(str+" "); } });
輸出結果:Hello 腳本之家 hello 網(wǎng)站
我們可以修改為如下代碼:
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("Hello"); list.add("腳本之家"); list.add("hello"); list.add("網(wǎng)站"); //表示調(diào)用一個,不帶有參數(shù)的方法,其執(zhí)行花括號內(nèi)的語句,為原來的函數(shù)體內(nèi)容。 list.forEach(s -> { System.out.println(s); }); }
輸出結果:Hello 腳本之家 hello 網(wǎng)站
List接口
sort()方法的演示
sort方法源碼:該方法根據(jù)c指定的比較規(guī)則對容器元素進行排序。
public void sort(Comparator<? super E> c) { final int expectedModCount = modCount; Arrays.sort((E[]) elementData, 0, size, c); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } modCount++; }
使用示例:
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("Hello"); list.add("腳本之家"); list.add("hello"); list.add("網(wǎng)站"); list.sort(new Comparator<String>() { @Override public int compare(String str1, String str2){ //注意這里比較長度 return str1.length()-str2.length(); } }); System.out.println(list); }
輸出結果:腳本之家, Hello, hello, 網(wǎng)站
修改為lambda表達式:
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("Hello"); list.add("腳本之家"); list.add("hello"); list.add("網(wǎng)站"); //調(diào)用帶有2個參數(shù)的方法,且返回長度的差值 list.sort((str1,str2)-> str1.length()-str2.length()); System.out.println(list); }
輸出結果:腳本之家, Hello, hello, 網(wǎng)站
Map接口
HashMap 的 forEach()
該方法原型如下:
default void forEach(BiConsumer<? super K, ? super V> action) {Objects.requireNonNull(action); for (Map.Entry<K, V> entry : entrySet()) { K k; V v; try { k = entry.getKey(); v = entry.getValue(); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } action.accept(k, v); } }
作用是對Map中的每個映射執(zhí)行action指定的操作。
代碼示例:
public static void main(String[] args) { HashMap<Integer, String> map = new HashMap<>(); map.put(1, "hello"); map.put(2, "腳本之家"); map.put(3, "hello"); map.put(4, "網(wǎng)站"); map.forEach(new BiConsumer<Integer, String>(){ @Override public void accept(Integer k, String v){ System.out.println(k + "=" + v); } }); }
輸出結果:
1=hello 2=腳本之家 3=hello 4=網(wǎng)站
使用lambda表達式后的代碼:
public static void main(String[] args) { HashMap<Integer, String> map = new HashMap<>(); map.put(1, "hello"); map.put(2, "腳本之家"); map.put(3, "hello"); map.put(4, "網(wǎng)站"); map.forEach((k,v)-> System.out.println(k + "=" + v)); }
輸出結果:
1=hello 2=腳本之家 3=hello 4=網(wǎng)站
Lambda表達式總結
Lambda表達式的優(yōu)點很明顯,在代碼層次上來說,使代碼變得非常的簡潔。缺點也很明顯,代碼不易讀。
優(yōu)點:
- 代碼簡潔,開發(fā)迅速
- 方便函數(shù)式編程
- 非常容易進行并行計算
- Java 引入 Lambda,改善了集合操作
缺點:
- 代碼可讀性變差
- 在非并行計算中,很多計算未必有傳統(tǒng)的 for 性能要高
- 不容易進行調(diào)試
到此這篇關于JavaSE中Lambda表達式的使用與變量捕獲的文章就介紹到這了,更多相關JavaSE Lambda表達式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
解決Java編譯時錯誤:A JNI error has occurred,ple
這篇文章主要介紹了解決Java編譯時錯誤:A JNI error has occurred,please check your installation and try again,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02使用MyBatis-Plus實現(xiàn)聯(lián)表查詢分頁的示例代碼
本文主要講述了如何在SpringBoot項目中使用MyBatis-Plus的分頁插件,通過這個示例,可以學會如何利用MyBatis-Plus進行高效的分頁查詢,感興趣的可以了解一下2024-10-10mybatis mapper.xml獲取insert后的自增ID問題
這篇文章主要介紹了mybatis mapper.xml獲取insert后的自增ID問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05