java中l(wèi)ambda表達(dá)式語(yǔ)法說(shuō)明
語(yǔ)法說(shuō)明
一個(gè)lambda表達(dá)式由如下幾個(gè)部分組成:
1. 在圓括號(hào)中以逗號(hào)分隔的形參列表。在CheckPerson.test方法中包含一個(gè)參數(shù)p,代表了一個(gè)Person類的實(shí)例。注意:lambda表達(dá)式中的參數(shù)的類型是可以省略的;此外,如果只有一個(gè)參數(shù)的話連括號(hào)也是可以省略的。比如上一節(jié)曾提到的代碼:
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
2. 箭頭符號(hào):->。用來(lái)分隔參數(shù)和函數(shù)體。
3. 函數(shù)體。由一個(gè)表達(dá)式或代碼塊組成。在上一節(jié)的例子中使用了這樣的表達(dá)式:
p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
如果使用的是表達(dá)式,java運(yùn)行時(shí)會(huì)計(jì)算并返回表達(dá)式的值。另外,還可以選擇在代碼塊中使用return語(yǔ)句:
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
不過(guò)return語(yǔ)句并不是表達(dá)式。在lambda表達(dá)式中需要將語(yǔ)句用花括號(hào)括起來(lái),然而卻沒(méi)有必要在只是調(diào)用一個(gè)返回值為空的方法時(shí)也用花括號(hào)括起來(lái),所以如下的寫法也是正確的:
email -> System.out.println(email)
lambda表達(dá)式和方法的聲明看起來(lái)有很多類似的地方。所以也可以把lambda表達(dá)式視為匿名方法,也就是沒(méi)有定義名字的方法。
以上提到的lambda表達(dá)式都是只使用了一個(gè)參數(shù)作為形參的表達(dá)式。下面的實(shí)例類,Caulator,演示了如何使用多個(gè)參數(shù)作為形參:
package com.zhyea.zytools;
public class Calculator {
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) {
Calculator myApp = new Calculator();
IntegerMath addition = (a, b) -> a + b;
IntegerMath subtraction = (a, b) -> a - b;
System.out.println("40 + 2 = " + myApp.operateBinary(40, 2, addition));
System.out.println("20 - 10 = " + myApp.operateBinary(20, 10, subtraction));
}
}
代碼中operateBinary方法使用了兩個(gè)整型參數(shù)執(zhí)行算數(shù)操作。這里的算數(shù)操作本身就是IntegerMath接口的一個(gè)實(shí)例。在上面的程序中使用lambda表達(dá)式定義了兩個(gè)算數(shù)操作:addition和subtraction。執(zhí)行程序會(huì)打印如下內(nèi)容:
40 + 2 = 42 20 - 10 = 10
訪問(wèn)外部類的局部變量
類似于局部類或匿名類,lambda表達(dá)式也可以訪問(wèn)外部類的局部變量。不同的是,使用lambda表達(dá)式時(shí)無(wú)需考慮覆蓋之類的問(wèn)題。lambda表達(dá)式只是一個(gè)詞法上的概念,這意味著它不需要從超類中繼承任何名稱,也不會(huì)引入新的作用域。也就是說(shuō),在lambda表達(dá)式中的聲明和在它的外部環(huán)境中的聲明意義是一樣的。在下面的例子中對(duì)此作了演示:
package com.zhyea.zytools;
import java.util.function.Consumer;
public class LambdaScopeTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
//如下的語(yǔ)句會(huì)導(dǎo)致編譯器在statement A處報(bào)錯(cuò)“l(fā)ocal variables referenced from a lambda expression must be final or effectively final”
// x = 99;
Consumer<integer> myConsumer = (y) ->{
System.out.println("x = " + x); // Statement 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);
}
}
這段代碼會(huì)輸出如下內(nèi)容:
x = 23 y = 23 this.x = 1 LambdaScopeTest.this.x = 0
如果使用示例中l(wèi)ambda表達(dá)式myConsumer中的參數(shù)y替換為x,編譯器就會(huì)報(bào)錯(cuò):
Consumer<integer> myConsumer = (x) ->{
// ....
};
編譯器報(bào)錯(cuò)信息是:“variable x is already defined in method methodInFirstLevel(int)”,就是說(shuō)在方法methodInFirstLevel中已經(jīng)定義了變量x。報(bào)錯(cuò)是因?yàn)閘ambda表達(dá)式不會(huì)引入新的作用域。也因此呢,可以在lambda表達(dá)式中直接訪問(wèn)外部類的域字段、方法以及形參。在這個(gè)例子中,lambda表達(dá)式myConsumer直接訪問(wèn)了方法methodInFirstLevel的形參x。而訪問(wèn)外部類的成員時(shí)也是直接使用this關(guān)鍵字。在這個(gè)例子中this.x指的就是FirstLevel.x。
然而,和局部類或匿名類一樣,lambda表達(dá)式也只能訪問(wèn)局部變量或外部被聲明為final(或等同于final)的成員。比如,我們將示例代碼methodInFirstLevel方法中“x=99”前面的注釋去掉:
//如下的語(yǔ)句會(huì)導(dǎo)致編譯器在statement A處報(bào)錯(cuò)“l(fā)ocal variables referenced from a lambda expression must be final or effectively final”
x = 99;
Consumer<integer> myConsumer = (y) ->{
System.out.println("x = " + x); // Statement A
System.out.println("y = " + y);
System.out.println("this.x = " + this.x);
System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x);
};
因?yàn)樵谶@段語(yǔ)句中修改了參數(shù)x的值,使得methodInFirstLevel的參數(shù)x不可以再被視為final式的。因此java編譯器就會(huì)在lambda表達(dá)式訪問(wèn)局部變量x的地方報(bào)出類似“l(fā)ocal variables referenced from a lambda expression must be final or effectively final”這樣的錯(cuò)誤。
目標(biāo)類型
該如何判斷l(xiāng)ambda表達(dá)式的類型呢。再來(lái)看一下篩選適齡服兵役人員的代碼:
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
這段代碼在兩處用到過(guò):
public static void printPersons(List<Person> roster, CheckPerson tester) —— 方案三
public void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester) —— 方案六
在調(diào)用printPersons方法時(shí),這個(gè)方法期望一個(gè)CheckPerson 類型的參數(shù),此時(shí)上面的那個(gè)表達(dá)式就是一個(gè)CheckPerson 類型的表達(dá)式。在調(diào)用printPersonsWithPredicate方法時(shí),期望一個(gè)Predicate<Person>類型的參數(shù),此時(shí)同樣的表達(dá)式就是Predicate<Person>類型的。像這樣子的,由方法期望的類型來(lái)決定的類型就叫做目標(biāo)類型(其實(shí)我覺(jué)得scala中的類型推斷用在這里更合適)。java編譯器就是通過(guò)目標(biāo)類型的上下文語(yǔ)境或者發(fā)現(xiàn)lambda表達(dá)式時(shí)的位置來(lái)判斷l(xiāng)ambda表達(dá)式的類型的。這也就意味著只能在Java編譯器可以推斷出類型的位置使用Lambda表達(dá)式:
- 變量聲明;
- 賦值;
- 返回語(yǔ)句;
- 數(shù)組初始化;
- 方法或者構(gòu)造器參數(shù);
- lambda表達(dá)式方法體;
- 條件表達(dá)式(?:);
- 拋出異常時(shí)。
目標(biāo)類型和方法參數(shù)
對(duì)于方法參數(shù),Java編譯器還需要依賴兩個(gè)語(yǔ)言特性來(lái)決定目標(biāo)類型:重載解析和類型參數(shù)推斷。
看一下下面的這兩個(gè)函數(shù)式接口( java.lang.Runnable and java.util.concurrent.Callable<V>):
public interface Runnable {
void run();
}
public interface Callable<v> {
V call();
}
Runnable.run()方法沒(méi)有返回值,而Callable.call()方法有。
假設(shè)我們像下面這樣重載了invoke方法:
void invoke(Runnable r) {
r.run();
}
<t> T invoke(Callable<t> c) {
return c.call();
}
那么在下面的語(yǔ)句中將會(huì)調(diào)用哪個(gè)方法呢:
String s = invoke(() -> "done");
調(diào)用的是invoke(Callable<T>),因?yàn)檫@個(gè)方法有返回值,而invoke(Runnable<T>)沒(méi)有返回值。在這種情況下lambda表達(dá)式(() -> “done”)的類型是Callable<T>。
序列化
如果一個(gè)lambda表達(dá)式的目標(biāo)類型還有它調(diào)用的參數(shù)的類型都是可序列化的,那么lambda表達(dá)式也是可序列化的。然而就像內(nèi)部類一樣,強(qiáng)烈不建議對(duì)lambda表達(dá)式進(jìn)行序列化。
- Java編程中使用lambda表達(dá)式的奇技淫巧
- Java Lambda表達(dá)式詳解和實(shí)例
- Java8新特性lambda表達(dá)式有什么用(用法實(shí)例)
- java使用lambda表達(dá)式對(duì)List集合進(jìn)行操作技巧(JDK1.8)
- Java8新特性之Lambda表達(dá)式淺析
- Java8中l(wèi)ambda表達(dá)式的應(yīng)用及一些泛型相關(guān)知識(shí)
- 在java中 利用匿名內(nèi)部類進(jìn)行較簡(jiǎn)潔的雙括弧初始化的方法
- java中匿名內(nèi)部類詳解
- Java中的匿名內(nèi)部類小結(jié)
- Java匿名對(duì)象與匿名內(nèi)部類
- 詳談Java編程之委托代理回調(diào)、內(nèi)部類以及匿名內(nèi)部類回調(diào)(閉包回調(diào))
- Java Lambda表達(dá)式與匿名內(nèi)部類的聯(lián)系和區(qū)別實(shí)例分析
相關(guān)文章
基于SpringBoot實(shí)現(xiàn)IP黑白名單的詳細(xì)步驟
IP黑白名單是網(wǎng)絡(luò)安全管理中常見(jiàn)的策略工具,用于控制網(wǎng)絡(luò)訪問(wèn)權(quán)限,根據(jù)業(yè)務(wù)場(chǎng)景的不同,其應(yīng)用范圍廣泛,比如比較容易被盜刷的短信接口、文件接口,都需要添加IP黑白名單加以限制,所以本文給大家介紹了基于SpringBoot實(shí)現(xiàn)IP黑白名單的詳細(xì)步驟,需要的朋友可以參考下2024-01-01
Java Comparable 和 Comparator 的詳解及區(qū)別
這篇文章主要介紹了Java Comparable 和 Comparator 的詳解及區(qū)別的相關(guān)資料,Comparable 自然排序和Comparator 定制排序的實(shí)例,需要的朋友可以參考下2016-12-12
Java中將String類型依照某個(gè)字符分割成數(shù)組的方法
下面小編就為大家分享一篇Java中將String類型依照某個(gè)字符分割成數(shù)組的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
Java 利用枚舉實(shí)現(xiàn)接口進(jìn)行統(tǒng)一管理
這篇文章主要介紹了Java 利用枚舉實(shí)現(xiàn)接口進(jìn)行統(tǒng)一管理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
Spring中@Autowired注解作用在方法上和屬性上說(shuō)明
這篇文章主要介紹了Spring中@Autowired注解作用在方法上和屬性上說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
java String 轉(zhuǎn)成Double二維數(shù)組的方法
下面小編就為大家?guī)?lái)一篇java String 轉(zhuǎn)成Double二維數(shù)組的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
Spring框架開(kāi)發(fā)scope作用域分析總結(jié)
這篇文章主要介紹了Spring框架開(kāi)發(fā)中scope作用域的分析總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-09-09

