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