摘要:表達(dá)式的主要作用就是代替匿名內(nèi)部類的煩瑣語法。從這點來看,表達(dá)式的代碼塊與匿名內(nèi)部類的方法體是相同的。與匿名內(nèi)部類相似的是,由于表達(dá)式訪問了局部變量,該局部變量相當(dāng)于與一個隱式的修飾,因此不允許對局部變量重新賦值。
函數(shù)式接口
函數(shù)式接口(Functional Interface)就是一個只有一個抽象方法(可以包含多個默認(rèn)方法或多個static方法)的普通接口,可以被隱式轉(zhuǎn)換為lambda表達(dá)式,可以現(xiàn)有的函數(shù)友好地支持 lambda。
函數(shù)式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
java.util.function
Lambda表達(dá)式入門匿名內(nèi)部類:
public class CommandTest2 { public static void main(String[] args) { ProcessArray pa = new ProcessArray(); int[] target = {3, -4, 6, 4}; //處理數(shù)組,具體處理行為取決于匿名內(nèi)部類 pa.process(target, new Command() { public void process(int[] target) { int sum = 0; for(int tmp :target) { sum += tmp; } System.out.println("數(shù)組元素的總和是:"+ sum); } }); } }
Lambda表達(dá)式:
public class CommandTest3 { public static void main(String[] args) { ProcessArray pa = new ProcessArray(); int[] array = {3, -4, 6, 4}; //處理數(shù)組,具體處理行為取決于匿名內(nèi)部類 pa.process(array, (int[] target)->{ int sum = 0; for(int tmp : target) { sum += tmp; } System.out.println("數(shù)組元素的總和是:"+ sum); }); } }
當(dāng)使用Lambda表達(dá)式代替匿名內(nèi)部類創(chuàng)建對象時,Lambda表達(dá)式的代碼塊將會代替實現(xiàn)抽象方法的方法體,Lambda表達(dá)式就相當(dāng)一個匿名方法。
Lambda表達(dá)式的主要作用就是代替匿名內(nèi)部類的煩瑣語法。它由三部分組成。
形參列表。形參列表允許省略形參類型。如果形參列表只有一個參數(shù),甚至連形參列表的圓括號也可以省略。
箭頭(->)。必須通過英文中畫線號和大于符號組成。
代碼塊。如果代碼塊只包含一條語句,Lambda表達(dá)式允許省略代碼塊的花括號,那么這條語句就不用用花括號表達(dá)語句結(jié)束。Lambda代碼塊只有一條return語句,甚至可以省略return關(guān)鍵字。Lambda表達(dá)式需要返回值,而它的代碼塊中僅有一條省略了return的語句,Lambda表達(dá)式會自動返回這條語句的值。
interface Eatable { void test(); } interface Flyable { void fly(String weather); } interface Addable { int add(int a, int b); } public class LambdaQs { //調(diào)用該方法需要Eatable對象 public void eat(Eatable e) { System.out.println(e); e.test(); } //調(diào)用該方法需要Flyable對象 public void drive(Flyable f) { System.out.println("老司機(jī)正在開:"+f); f.fly("亮瞎眼"); } //調(diào)用該方法需要Addable對象 public void test(Addable add) { System.out.println("34與59的和為:"+add.add(34, 59)); } public static void main(String[] args) { LambdaQs lq = new LambdaQs(); //Lamba表達(dá)式的代碼塊只有一條語句,可以省略花括號 lq.eat(()->System.out.println("蘋果很贊哦!")); //Lamba表達(dá)式的形參列表只有一個形參,可以省略圓括號 lq.drive(weather -> { System.out.println("今天天氣是:"+weather); System.out.println("直升機(jī)飛行平穩(wěn)"); }); //Lambda表達(dá)式的代碼塊只有一條語句,可以省略花括號 //代碼塊中只有一條語句,即使該表達(dá)式需要返回值,也可以省略return關(guān)鍵字 lq.test((a, b) -> a + b); } }Lambda表達(dá)式與函數(shù)式接口
Lambda表達(dá)式的類型,也被稱為“目標(biāo)類型”,Lambda表達(dá)式的目標(biāo)類型必須是“函數(shù)式接口”。函數(shù)式接口代表只包含一個抽象方法的接口。函數(shù)式接口可以包含多個默認(rèn)方法、類方法,但只能聲明一個抽象方法
如果采用匿名內(nèi)部類語法來創(chuàng)建函數(shù)式接口的實例,則只需要實現(xiàn)一個抽象方法,在這種情況下即可采用Lambda表達(dá)式來創(chuàng)建對象,該表達(dá)式創(chuàng)建處理的對象的目標(biāo)類型就是這個函數(shù)式接口。查詢Java8的API文檔,可以發(fā)現(xiàn)大量的函數(shù)式接口,例如:Runnable、ActionListener等接口都是函數(shù)式接口
@FunctionalInterface注解,該注解通常放在接口定義前面,該注解對程序功能沒有任何作用,它用于告訴編譯器執(zhí)行更嚴(yán)格檢查——檢查該接口必須是函數(shù)式接口,否則編譯器就會報錯
Lambda表達(dá)式實現(xiàn)的是匿名方法——因此它只能實現(xiàn)特定函數(shù)式接口中的唯一方法。這意味著Lambda表達(dá)式有如下兩個限制:
Lambda表達(dá)式的目標(biāo)類型必須是明確的函數(shù)式接口
Lambda表達(dá)式只能為函數(shù)式接口創(chuàng)建對象。Lambda表達(dá)式只能實現(xiàn)一個方法,因此它只能為只有一個抽象方法的接口(函數(shù)式接口)創(chuàng)建對象
為了保證Lambda表達(dá)式的目標(biāo)類型是一個明確的函數(shù)式接口,可以有如下三種常見方式:
將Lambda表達(dá)式賦值給函數(shù)式接口類型的變量
將Lambda表達(dá)式作為函數(shù)式接口類型的參數(shù)傳給某個方法
使用函數(shù)式接口對Lambda表達(dá)式進(jìn)行強(qiáng)制類型轉(zhuǎn)換
同樣的Lambda表達(dá)式的目標(biāo)類型完全可能是變化的——唯一的要求是,Lambda表達(dá)式實現(xiàn)的匿名方法與目標(biāo)類型(函數(shù)式接口)中唯一的抽象方法有相同的形參列表。
Java8在java.util.function包下預(yù)定義了大量函數(shù)式接口,典型地包含如下4類接口。
XxxFunction:這類接口中通常包含一個apply()抽象方法,該方法對參數(shù)進(jìn)行處理、轉(zhuǎn)換(apply()方法的處理邏輯由Lambda表達(dá)式來實現(xiàn)),然后返回一個新的值。該函數(shù)式接口通常用于對指定數(shù)據(jù)進(jìn)行轉(zhuǎn)換處理。
XxxConsumer:這類接口中通常包含一個accept()抽象方法,該方法與XxxFunction接口中的apply()方法基本相似,也負(fù)責(zé)對參數(shù)進(jìn)行處理,只是該方法不會返回處理結(jié)果。
XxxxPredicate:這類接口中通常包含一個test()抽象方法,該方法通常用來對參數(shù)進(jìn)行某種判斷(test()方法的判斷邏輯由Lambda表達(dá)式來實現(xiàn)),然后返回一個boolean值。該接口通常用于判斷參數(shù)是否滿足特定條件,經(jīng)常用于進(jìn)行篩濾數(shù)據(jù)。
XxxSupplier:這類接口中通常包含一個getAsXxx()抽象方法,該方法不需要輸入?yún)?shù),該方法會按某種邏輯算法(getAsXxx()方法的邏輯算法由Lambda表達(dá)式來實現(xiàn))返回一個數(shù)據(jù)。
方法引用與構(gòu)造器引用如果Lambda表達(dá)式的代碼塊只有一條代碼,還可以在代碼塊中使用方法引用和構(gòu)造器引用。
方法引用和構(gòu)造器引用可以讓Lambda表達(dá)式的代碼塊更加簡潔。方法引用和構(gòu)造器引用都需要使用兩個英文冒號。
種類 | 示例 | 說明 | 對應(yīng)的Lambda表達(dá)式 |
---|---|---|---|
引用類方法 | 類名::類方法 | 函數(shù)式接口中被實現(xiàn)方法的全部參數(shù)傳給該類方法作為參數(shù) | (a,b,...)->類名.類方法(a,b,...) |
引用特定對象的實例方法 | 特定對象::實例方法 | 函數(shù)式接口中被實現(xiàn)方法的全部參數(shù)傳給該類方法作為參數(shù) | (a,b,...)->特定對象.實例方法(a,b,...) |
引用某類對象的實例方法 | 類名::實例方法 | 函數(shù)式接口中被實現(xiàn)方法的第一個參數(shù)作為調(diào)用者,后面的參數(shù)全部傳給該方法作為參數(shù) | (a,b,...)->a.實例方法(b,...) |
引用構(gòu)造器 | 類名::new | 函數(shù)式接口中被實現(xiàn)方法的全部參數(shù)傳給該類方法作為參數(shù) | (a,b,...)->new 類名(a,b,...) |
@FunctionalInterface interface Converter{ Integer convert(String from); }
該函數(shù)式接口包含一個convert()抽象方法,該方法負(fù)責(zé)將String參數(shù)轉(zhuǎn)換成Integer。下面代碼使用Lambda表達(dá)式來創(chuàng)建Converter對象。
//下面代碼使用Lambda表達(dá)式創(chuàng)建Converter對象 Converter converter1 = from -> Integer.valueOf(from);
上面Lambda表達(dá)式的代碼塊只有一條語句,因此程序省略了該代碼塊的花括號;而且由于表達(dá)式所實現(xiàn)的convert()方法需要返回值,因此Lambda表達(dá)式將會把這條代碼的值作為返回值
調(diào)用convert1對象的convert()方法將字符串轉(zhuǎn)換為整數(shù)了,例如如下代碼
Integer val = converter1.convert("99"); System.out.println(val); // 輸出整數(shù)99
上面Lambda表達(dá)式的代碼塊只有一行調(diào)用類方法的代碼,因此可以使用如下方法引用進(jìn)行替換
// 方法引用代替Lambda表達(dá)式:引用類方法。 // 函數(shù)式接口中被實現(xiàn)方法的全部參數(shù)傳給該類方法作為參數(shù)。 Converter converter1 = Integer::valueOf;
對于上面的類方法引用,也就是調(diào)用Integer類的valueOf()類方法來實現(xiàn)Converter函數(shù)式接口中唯一的抽象方法,當(dāng)調(diào)用Converter接口中唯一的抽象方法時,調(diào)用參數(shù)將會傳給Integer類的valueOf()類方法。
引用特定對象的實例方法// 下面代碼使用Lambda表達(dá)式創(chuàng)建Converter對象 Converter converter2 = from -> "fkit.org".indexOf(from); Integer value = converter2.convert("it"); System.out.println(value); // 輸出2
// 方法引用代替Lambda表達(dá)式:引用特定對象的實例方法。 // 函數(shù)式接口中被實現(xiàn)方法的全部參數(shù)傳給該方法作為參數(shù)。 Converter converter2 = "fkit.org"::indexOf;
對于上面的實例方法引用,也就是調(diào)用"fkit.org"對象的indexOf()實例方法來實現(xiàn)Converter函數(shù)式接口中唯一的抽象方法,當(dāng)調(diào)用Converter接口中唯一的抽象方法時,調(diào)用參數(shù)將會傳給"fkit.org"對象的indexOf()實例方法。
引用某類對象的實例方法定義如下函數(shù)式接口
@FunctionalInterface interface MyTest { String test(String a , int b , int c); }
使用Lambda表達(dá)式來創(chuàng)建一個MyTest對象
MyTest mt = (a , b , c) -> a.substring(b , c); String str = mt.test("Java I Love you" , 2 , 9); System.out.println(str); // 輸出:va I Lo
// 方法引用代替Lambda表達(dá)式:引用某類對象的實例方法。 // 函數(shù)式接口中被實現(xiàn)方法的第一個參數(shù)作為調(diào)用者, // 后面的參數(shù)全部傳給該方法作為參數(shù)。 MyTest mt = String::substring;引用構(gòu)造器
@FunctionalInterface interface YourTest { JFrame win(String title); }
下面代碼使用Lambda表達(dá)式創(chuàng)建YourTest對象
YourTest yt = (String a) -> new JFrame(a);
JFrame jf = yt.win("我的窗口"); System.out.println(jf);
// 構(gòu)造器引用代替Lambda表達(dá)式。 // 函數(shù)式接口中被實現(xiàn)方法的全部參數(shù)傳給該構(gòu)造器作為參數(shù)。 YourTest yt = JFrame::new;
對于上面的構(gòu)造器引用,也就是調(diào)用某個JFrame類的構(gòu)造器來實現(xiàn)YourTest函數(shù)式接口中唯一的抽象方法,當(dāng)調(diào)用YourTest接口中的唯一的抽象方法時,調(diào)用參數(shù)將會傳給JFrame構(gòu)造器。調(diào)用YourTest對象的win()抽象方法時,實際只傳入了一個String類型的參數(shù),這個String類型的參數(shù)會被傳給JFrame構(gòu)造器——這就確定了調(diào)用JFrame類的、帶一個String參數(shù)的構(gòu)造器。
Lambda表達(dá)式與匿名內(nèi)部類的聯(lián)系和區(qū)別Lambda表達(dá)式是匿名內(nèi)部類的一種簡化,存在如下相同點
Lambda表達(dá)式與匿名內(nèi)部類一樣,都可以直接訪問“effectively final”的局部變量,以及外部類的成員變量(包括實例變量和類變量)。
Lambda表達(dá)式創(chuàng)建的對象與匿名內(nèi)部類生成的對象一樣,都可以直接調(diào)用從接口中繼承的默認(rèn)方法。
@FunctionalInterface interface Displayable { //定義一個抽象方法和默認(rèn)方法 void display(); default int add(int a, int b) { return a + b; } } public class LambdaAndInner { private int age = 24; private static String name = "簡單點,說話的方式簡單點" ; public void test() { String sing = "演員"; Displayable dis = () -> { //訪問“effectively final”的局部變量 System.out.println("sing局部變量為:"+ sing); //訪問外部類的實例變量和類變量 System.out.println("外部類的age實例變量為:"+ age); System.out.println("外部類的name類變量為:"+ name); }; dis.display(); //調(diào)用dis對象從接口中繼承的add()方法 System.out.println(dis.add(34, 59)); } public static void main(String[] args) { LambdaAndInner lambdaAndInner = new LambdaAndInner(); lambdaAndInner.test(); } }
上面Lambda表達(dá)式創(chuàng)建了一個Display的對象,Lambda表達(dá)式的代碼塊中的三行粗體字代碼分別示范了訪問“effectively fianl”的局部變量、外部類的實例變量和類變量。從這點來看,Lambda表達(dá)式的代碼塊與匿名內(nèi)部類的方法體是相同的。
與匿名內(nèi)部類相似的是,由于Lambda表達(dá)式訪問了sing局部變量,該局部變量相當(dāng)于與一個隱式的final修飾,因此不允許對sing局部變量重新賦值。
Lambda表達(dá)式與匿名內(nèi)部類主要存在如下區(qū)別
匿名內(nèi)部類可以為任意接口創(chuàng)建實例——不管接口包含多少個抽象方法,只有匿名內(nèi)部類實現(xiàn)所有的抽象方法即可;但Lambda表達(dá)式只能為函數(shù)式接口創(chuàng)建實例。
匿名內(nèi)部類可以為抽象類甚至普通類創(chuàng)建實例;但Lambda表達(dá)式只能為函數(shù)式接口創(chuàng)建實例。
匿名內(nèi)部類實現(xiàn)的抽象方法的方法體允許調(diào)用接口中定義的默認(rèn)方法;但Lambda表達(dá)式的代碼塊不允許調(diào)用默認(rèn)方法。
使用Lambda表達(dá)式調(diào)用Arrays的類方法Arrays類的有些方法需要Comparator、XxxOperator、XxxFunction等接口的實例,這些接口都是函數(shù)式接口,因此可以使用Lambda表達(dá)式來調(diào)用Arrays的方法
import java.util.Arrays; import javax.management.openmbean.OpenDataException; public class LambdaArrays { public static void main(String[] args) { String arr1[] = new String[]{"皇家馬德里", "巴塞羅那", "巴黎圣日耳曼","尤文圖斯","切爾西"}; Arrays.parallelSort(arr1, (o1, o2) -> o1.length() - o2.length()); System.out.println(Arrays.toString(arr1)); int[] arr2 = new int[]{4, 2, 1, 3, 5}; //left代表數(shù)組中前一個索引處的元素,計算第一個元素時,left為1 //right代表數(shù)組中當(dāng)前索引處的元素 Arrays.parallelPrefix(arr2, (left, right) -> left * right); System.out.println(Arrays.toString(arr2)); long[] arr3 = new long[5]; //operand代表正在計算的元素索引 Arrays.parallelSetAll(arr3, operand -> operand * 5); System.out.println(Arrays.toString(arr3)); } }
(o1, o2) -> o1.length() - o2.length():目標(biāo)類型是Comparator指定了判斷字符串大小的標(biāo)準(zhǔn):字符串越長,即可認(rèn)為該字符串越大
(left, right) -> left * right:目標(biāo)類型是IntBinaryOperator,該對象將會根據(jù)前后兩個元素來計算當(dāng)前元素的值
operand -> operand * 5::目標(biāo)類型是IntToLongFunction,該對象將會根據(jù)元素的索引來計算當(dāng)前元素的值
Lambda表達(dá)式可以讓程序更加簡潔。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/66304.html
摘要:在支持一類函數(shù)的語言中,表達(dá)式的類型將是函數(shù)。匿名函數(shù)的返回類型與該主體表達(dá)式一致如果表達(dá)式的主體包含一條以上語句,則表達(dá)式必須包含在花括號中形成代碼塊。注意,使用表達(dá)式的方法不止一種。 摘要:此篇文章主要介紹 Java8 Lambda 表達(dá)式產(chǎn)生的背景和用法,以及 Lambda 表達(dá)式與匿名類的不同等。本文系 OneAPM 工程師編譯整理。 Java 是一流的面向?qū)ο笳Z言,除了部分簡...
摘要:表達(dá)式簡介表達(dá)式是一個匿名函數(shù)對于而言并不很準(zhǔn)確,但這里我們不糾結(jié)這個問題。如果表達(dá)式的正文有一條以上的語句必須包含在大括號代碼塊中,且表達(dá)式的返回值類型要與匿名函數(shù)的返回類型相同。 版權(quán)聲明:本文由吳仙杰創(chuàng)作整理,轉(zhuǎn)載請注明出處:https://segmentfault.com/a/1190000009186509 1. 引言 在 Java 8 以前,若我們想要把某些功能傳遞給某些方...
摘要:初體驗下面進(jìn)入本文的正題表達(dá)式。接下來展示表達(dá)式和其好基友的配合。吐槽一下方法引用表面上看起來方法引用和構(gòu)造器引用進(jìn)一步簡化了表達(dá)式的書寫,但是個人覺得這方面沒有的下劃線語法更加通用。 感謝同事【天錦】的投稿。投稿請聯(lián)系 [email protected] 本文主要記錄自己學(xué)習(xí)Java8的歷程,方便大家一起探討和自己的備忘。因為本人也是剛剛開始學(xué)習(xí)Java8,所以文中肯定有錯誤和理解偏...
摘要:語言是強(qiáng)類型面向?qū)ο蟮恼Z言,所以必須提供一種數(shù)據(jù)類型作為表達(dá)式的返回值類型符合中函數(shù)格式的定義符合面向?qū)ο笠?guī)則,所以最終表達(dá)式要有一個映射成對象的過程。定一個函數(shù)式接口我們在接口里定義了一個沒有參數(shù)返回值的抽象方法。 在JAVA中,Lambda 表達(dá)式(Lambda expression)是一個抽象方法的實現(xiàn)。這個抽象方法必須是在接口中聲明的,而且實現(xiàn)類只需要實現(xiàn)這一個抽象方法,我們稱...
摘要:表達(dá)式又名閉包匿名函數(shù)筆記根據(jù)終于在中引入了表達(dá)式。函數(shù)式接口要介紹中表達(dá)式的實現(xiàn),需要知道什么是函數(shù)式接口。但同樣需要保證外部的自由變量不能在表達(dá)式中被改變。 Java Lambda 表達(dá)式(又名閉包 (Closure)/ 匿名函數(shù) ) 筆記 根據(jù) JSR 335, Java 終于在 Java 8 中引入了 Lambda 表達(dá)式。也稱之為閉包或者匿名函數(shù)。 showImg(https...
閱讀 1447·2021-11-22 15:11
閱讀 2862·2019-08-30 14:16
閱讀 2782·2019-08-29 15:21
閱讀 2935·2019-08-29 15:11
閱讀 2477·2019-08-29 13:19
閱讀 3010·2019-08-29 12:25
閱讀 443·2019-08-29 12:21
閱讀 2854·2019-08-29 11:03