Java lambda expression
Lambda表達(dá)式
匿名類的一個(gè)問(wèn)題是,如果匿名類的實(shí)現(xiàn)非常簡(jiǎn)單,例如只包含一個(gè)方法的接口,那么匿名類的語(yǔ)法可能看起來(lái)不實(shí)用且不清楚。在這些情況下,您通常會(huì)嘗試將功能作為參數(shù)傳遞給另一個(gè)方法,例如當(dāng)有人單擊按鈕時(shí)應(yīng)采取的操作。Lambda表達(dá)式使您可以執(zhí)行此操作,將功能視為方法參數(shù),或?qū)⒋a視為數(shù)據(jù)。
一頁(yè)???小道???下一頁(yè)?Java教程是為JDK 8編寫(xiě)的。本頁(yè)描述的示例和實(shí)踐沒(méi)有利用后續(xù)版本中引入的改進(jìn)。
Lambda表達(dá)式
匿名類的一個(gè)問(wèn)題是,如果匿名類的實(shí)現(xiàn)非常簡(jiǎn)單,例如只包含一個(gè)方法的接口,那么匿名類的語(yǔ)法可能看起來(lái)不實(shí)用且不清楚。在這些情況下,您通常會(huì)嘗試將功能作為參數(shù)傳遞給另一個(gè)方法,例如當(dāng)有人單擊按鈕時(shí)應(yīng)采取的操作。Lambda表達(dá)式使您可以執(zhí)行此操作,將功能視為方法參數(shù),或?qū)⒋a視為數(shù)據(jù)。
上一節(jié)“?匿名類?”向您展示了如何在不給它命名的情況下實(shí)現(xiàn)基類。雖然這通常比命名類更簡(jiǎn)潔,但對(duì)于只有一個(gè)方法的類,即使是匿名類也似乎有點(diǎn)過(guò)分和繁瑣。Lambda表達(dá)式允許您更緊湊地表達(dá)單方法類的實(shí)例。
Lambda表達(dá)式的理想用例
假設(shè)您正在創(chuàng)建社交網(wǎng)絡(luò)應(yīng)用程序。您希望創(chuàng)建一項(xiàng)功能,使管理員能夠?qū)M足特定條件的社交網(wǎng)絡(luò)應(yīng)用程序成員執(zhí)行任何類型的操作,例如發(fā)送消息。下表詳細(xì)描述了此用例:
| 名稱 | 對(duì)選定的成員執(zhí)行操作 |
| 主要演員 | 管理員 |
| 前提條件 | 管理員已登錄系統(tǒng)。 |
| 后置條件 | 僅對(duì)符合指定條件的成員執(zhí)行操作。 |
| 主要成功案例 | |
| 擴(kuò)展 | 1A。管理員可以選擇在指定要執(zhí)行的操作之前或選擇“?提交”按鈕之前預(yù)覽符合指定條件的成員。 |
| 發(fā)生頻率 | 白天很多次。 |
假設(shè)此社交網(wǎng)絡(luò)應(yīng)用程序的成員由以下Person類表示?:
公共類人{(lán)public enum Sex {男,女}字符串名稱;LocalDate生日;性別;字符串emailAddress;public int getAge(){// ...}public void printPerson(){// ...} }假設(shè)您的社交網(wǎng)絡(luò)應(yīng)用程序的成員存儲(chǔ)在一個(gè)List<Person>實(shí)例中。方法我介紹一種,其他的可以查看其官網(wǎng)。
創(chuàng)建搜索匹配一個(gè)特征的成員的方法
一種簡(jiǎn)單的方法是創(chuàng)建幾種方法;?每種方法都會(huì)搜索與一個(gè)特征匹配的成員,例如性別或年齡。以下方法打印超過(guò)指定年齡的成員:
public static void printPersonsOlderThan(List <Person> roster,int age){for(Person p:roster){if(p.getAge()> = age){p.printPerson();}} }注意:A?List是有序的?Collection。甲集合是一個(gè)對(duì)象,該組中的多個(gè)元素到單個(gè)單元中。集合用于存儲(chǔ),檢索,操作和傳遞聚合數(shù)據(jù)。有關(guān)集合的更多信息,請(qǐng)參閱?集合跟蹤。
這種方法可能會(huì)使您的應(yīng)用程序變得脆弱,這是由于引入了更新(例如更新的數(shù)據(jù)類型)導(dǎo)致應(yīng)用程序無(wú)法工作的可能性。假設(shè)您升級(jí)應(yīng)用程序并更改Person類的結(jié)構(gòu),使其包含不同的成員變量;?也許該類記錄和測(cè)量年齡與不同的數(shù)據(jù)類型或算法。您必須重寫(xiě)大量API以適應(yīng)此更改。此外,這種方法是不必要的限制;?例如,如果您想要打印年齡小于某個(gè)年齡的成員,該怎么辦?
Lambda表達(dá)式的語(yǔ)法
lambda表達(dá)式包含以下內(nèi)容:
-
括號(hào)中用逗號(hào)分隔的形式參數(shù)列表。該CheckPerson.test方法包含一個(gè)參數(shù),?p表示Person該類的實(shí)例?。
注意:您可以省略lambda表達(dá)式中參數(shù)的數(shù)據(jù)類型。此外,如果只有一個(gè)參數(shù),則可以省略括號(hào)。例如,以下lambda表達(dá)式也是有效的:
p - > p.getGender()== Person.Sex.MALE && p.getAge()> = 18&& p.getAge()<= 25 -
箭頭標(biāo)記,?->
-
一個(gè)主體,由單個(gè)表達(dá)式或語(yǔ)句塊組成。此示例使用以下表達(dá)式:
p.getGender()== Person.Sex.MALE && p.getAge()> = 18&& p.getAge()<= 25如果指定單個(gè)表達(dá)式,則Java運(yùn)行時(shí)將計(jì)算表達(dá)式,然后返回其值。或者,您可以使用return語(yǔ)句:
p - > {return p.getGender()== Person.Sex.MALE&& p.getAge()> = 18&& p.getAge()<= 25; }return語(yǔ)句不是表達(dá)式;?在lambda表達(dá)式中,必須用braces({})括起語(yǔ)句。但是,您不必在大括號(hào)中包含void方法調(diào)用。例如,以下是有效的lambda表達(dá)式:
電子郵件 - > System.out.println(電子郵件)
請(qǐng)注意,lambda表達(dá)式看起來(lái)很像方法聲明;?您可以將lambda表達(dá)式視為匿名方法 - 沒(méi)有名稱的方法。
以下示例?Calculator是一個(gè)lambda表達(dá)式的示例,它采用多個(gè)形式參數(shù):
公共類計(jì)算器{interface IntegerMath {int operation(int a,int b); }public int operateBinary(int a,int b,IntegerMath op){return op.operation(a,b);}public static void main(String ... args){計(jì)算器myApp = new Calculator();IntegerMath add =(a,b) - > a + b;IntegerMath減法=(a,b) - > a - b;System.out.println(“40 + 2 =”+myApp.operateBinary(40,2,另外));System.out.println(“20 - 10 =”+myApp.operateBinary(20,10,減法)); } }該方法operateBinary對(duì)兩個(gè)整數(shù)操作數(shù)執(zhí)行數(shù)學(xué)運(yùn)算。操作本身由實(shí)例指定IntegerMath。的例子中定義了lambda表達(dá)式兩個(gè)操作,addition和subtraction。該示例打印以下內(nèi)容:
40 + 2 = 42 20 - 10 = 10訪問(wèn)封閉范圍的局部變量
像本地和匿名類一樣,lambda表達(dá)式可以?捕獲變量?;?它們對(duì)封閉范圍的局部變量具有相同的訪問(wèn)權(quán)限。但是,與本地和匿名類不同,lambda表達(dá)式?jīng)]有任何陰影問(wèn)題(有關(guān)更多信息,請(qǐng)參閱?陰影)。Lambda表達(dá)式是詞法范圍的。這意味著它們不會(huì)從超類型繼承任何名稱或引入新級(jí)別的范圍。lambda表達(dá)式中的聲明與封閉環(huán)境中的聲明一樣被解釋。以下示例?LambdaScopeTest演示了這一點(diǎn):
import java.util.function.Consumer;公共類LambdaScopeTest {public int x = 0;class FirstLevel {public int x = 1;void methodInFirstLevel(int x){//以下語(yǔ)句導(dǎo)致編譯器生成//錯(cuò)誤“從lambda表達(dá)式引用的局部變量//必須是最終的或有效的最終“在聲明A中://// x = 99;消費(fèi)者<整數(shù)> myConsumer =(y) - > {System.out.println(“x =”+ x); //聲明A.System.out.println(“y =”+ y);System.out.println(“this.x =”+ this.x);System.out.println(“LambdaScopeTest.this.x =”+LambdaScopeTest.this.x);};myConsumer.accept(X);}}public static void main(String ... args){LambdaScopeTest st = new LambdaScopeTest();LambdaScopeTest.FirstLevel fl = st.new FirstLevel();fl.methodInFirstLevel(23);} }此示例生成以下輸出:
x = 23 y = 23 this.x = 1 LambdaScopeTest.this.x = 0如果在lambda表達(dá)式的聲明中替換參數(shù)x代替,則編譯器會(huì)生成錯(cuò)誤:ymyConsumer
消費(fèi)者<整數(shù)> myConsumer =(x) - > {// ... }編譯器生成錯(cuò)誤“變量x已在方法methodInFirstLevel(int)中定義”,因?yàn)閘ambda表達(dá)式不會(huì)引入新的作用域級(jí)別。因此,您可以直接訪問(wèn)封閉范圍的字段,方法和局部變量。例如,lambda表達(dá)式直接訪問(wèn)x方法的參數(shù)methodInFirstLevel。要訪問(wèn)封閉類中的變量,請(qǐng)使用關(guān)鍵字this。在此示例中,this.x引用成員變量FirstLevel.x。
但是,與本地和匿名類一樣,lambda表達(dá)式只能訪問(wèn)最終或有效最終的封閉塊的局部變量和參數(shù)。例如,假設(shè)您在methodInFirstLevel定義語(yǔ)句后立即添加以下賦值語(yǔ)句:
void methodInFirstLevel(int x){x = 99;// ... }由于這個(gè)賦值語(yǔ)句,變量FirstLevel.x不再是有效的最終結(jié)果。因此,Java編譯器生成類似于“從lambda表達(dá)式引用的局部變量必須是final或者final final”的錯(cuò)誤消息,其中l(wèi)ambda表達(dá)式myConsumer嘗試訪問(wèn)FirstLevel.x變量:
System.out.println(“x =”+ x);目標(biāo)打字
你如何確定lambda表達(dá)式的類型?回想一下lambda表達(dá)式,它選擇了男性和年齡在18到25歲之間的成員:
p - > p.getGender()== Person.Sex.MALE&& p.getAge()> = 18&& p.getAge()<= 25這個(gè)lambda表達(dá)式用于以下兩種方法:
-
public static void printPersons(List<Person> roster, CheckPerson tester)在方法3:在局部類指定搜索條件碼
-
public void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester)在方法6:Lambda表達(dá)式使用標(biāo)準(zhǔn)的功能接口
當(dāng)Java運(yùn)行時(shí)調(diào)用該方法時(shí)printPersons,它期望數(shù)據(jù)類型為CheckPerson,因此lambda表達(dá)式屬于此類型。但是,當(dāng)Java運(yùn)行時(shí)調(diào)用該方法時(shí)printPersonsWithPredicate,它期望數(shù)據(jù)類型為Predicate<Person>,因此lambda表達(dá)式屬于此類型。這些方法所期望的數(shù)據(jù)類型稱為目標(biāo)類型。要確定lambda表達(dá)式的類型,Java編譯器將使用發(fā)現(xiàn)lambda表達(dá)式的上下文或情境的目標(biāo)類型。因此,您只能在Java編譯器可以確定目標(biāo)類型的情況下使用lambda表達(dá)式:
-
變量聲明
-
分配
-
退貨聲明
-
數(shù)組初始化器
-
方法或構(gòu)造函數(shù)參數(shù)
-
Lambda表達(dá)體
-
條件表達(dá)式,??:
-
轉(zhuǎn)換表達(dá)式
目標(biāo)類型和方法參數(shù)
對(duì)于方法參數(shù),Java編譯器使用另外兩種語(yǔ)言特性確定目標(biāo)類型:重載解析和類型參數(shù)推斷。
考慮以下兩個(gè)功能接口(?java.lang.Runnable和?java.util.concurrent.Callable<V>):
public interface Runnable {void run(); }公共接口Callable <V> {V call(); }該方法Runnable.run不返回值,而是返回值Callable<V>.call。
假設(shè)您已invoke按如下方式重載方法(有關(guān)重載方法的詳細(xì)信息,請(qǐng)參閱?定義方法):
void invoke(Runnable r){r.run(); }<T> T invoke(Callable <T> c){return c.call(); }將在以下語(yǔ)句中調(diào)用哪個(gè)方法?
String s = invoke(() - >“done”);該方法invoke(Callable<T>)將被調(diào)用因?yàn)樵摲椒ǚ祷氐闹??方法?invoke(Runnable)沒(méi)有。在這種情況下,lambda表達(dá)式的類型() -> "done"是Callable<T>。
序列化
如果lambda表達(dá)式的目標(biāo)類型及其捕獲的參數(shù)是可序列化的,則可以?序列化它。但是,與?內(nèi)部類一樣,強(qiáng)烈建議不要對(duì)lambda表達(dá)式進(jìn)行序列化。
各種方法參考
有四種方法參考:
| 參考靜態(tài)方法 | ContainingClass::staticMethodName |
| 引用特定對(duì)象的實(shí)例方法 | containingObject::instanceMethodName |
| 引用特定類型的任意對(duì)象的實(shí)例方法 | ContainingType::methodName |
| 引用構(gòu)造函數(shù) | ClassName::new |
參考靜態(tài)方法
方法引用Person::compareByAge是對(duì)靜態(tài)方法的引用。
引用特定對(duì)象的實(shí)例方法
以下是對(duì)特定對(duì)象的實(shí)例方法的引用示例:
class ComparisonProvider {public int compareByName(Person a,Person b){return a.getName()。compareTo(b.getName());}public int compareByAge(Person a,Person b){return a.getBirthday()。compareTo(b.getBirthday());} } ComparisonProvider myComparisonProvider = new ComparisonProvider(); Arrays.sort(rosterAsArray,myComparisonProvider :: compareByName);方法引用myComparisonProvider::compareByName調(diào)用compareByName作為對(duì)象一部分的方法myComparisonProvider。JRE推斷出方法類型參數(shù),在本例中是(Person, Person)。
對(duì)特定類型的任意對(duì)象的實(shí)例方法的引用
以下是對(duì)特定類型的任意對(duì)象的實(shí)例方法的引用示例:
String [] stringArray = {“芭芭拉”,“詹姆斯”,“瑪麗”,“約翰”,“Patricia”,“Robert”,“Michael”,“Linda”}; Arrays.sort(stringArray,String :: compareToIgnoreCase);方法引用的等效lambda表達(dá)式String::compareToIgnoreCase將具有形式參數(shù)列表(String a, String b),其中a和b是用于更好地描述此示例的任意名稱。方法引用將調(diào)用該方法a.compareToIgnoreCase(b)。
參考構(gòu)造函數(shù)
您可以使用名稱以與靜態(tài)方法相同的方式引用構(gòu)造函數(shù)new。以下方法將元素從一個(gè)集合復(fù)制到另一個(gè)集合:
public static <T,SOURCE擴(kuò)展Collection <T>,DEST擴(kuò)展Collection <T >>DEST transferElements(SOURCE sourceCollection,供應(yīng)商<DEST> collectionFactory){DEST result = collectionFactory.get();for(T t:sourceCollection){result.add(T);}返回結(jié)果; }功能接口Supplier包含一個(gè)get不帶參數(shù)并返回對(duì)象的方法。因此,您可以transferElements使用lambda表達(dá)式調(diào)用該方法,如下所示:
設(shè)置<Person> rosterSetLambda =transferElements(roster,() - > {return new HashSet <>();});您可以使用構(gòu)造函數(shù)引用代替lambda表達(dá)式,如下所示:
設(shè)置<Person> rosterSet = transferElements(roster,HashSet :: new);Java編譯器推斷您要?jiǎng)?chuàng)建HashSet包含類型元素的集合Person。或者,您可以按如下方式指定:
設(shè)置<Person> rosterSet = transferElements(名冊(cè),HashSet <Person> :: new);?
轉(zhuǎn)載于:https://www.cnblogs.com/xasdh/p/10762717.html
新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎(jiǎng)!定制產(chǎn)品紅包拿不停!總結(jié)
以上是生活随笔為你收集整理的Java lambda expression的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CSS3动画常用贝塞尔曲线-效果演示
- 下一篇: Java内置的观察者模式的使用