一罚勾、Lambda表達(dá)式
Lambda表達(dá)式(也稱為閉包
),lambda表達(dá)式本質(zhì)上是一個(gè)匿名方法
。它允許我們將函數(shù)當(dāng)成參數(shù)傳遞給某個(gè)方法吵血,或者把代碼本身當(dāng)作數(shù)據(jù)處理。
函數(shù)式開發(fā)者非常熟悉這些概念偷溺。很多JVM平臺(tái)上的語言(Groovy蹋辅、Scala等)從誕生之日就支持Lambda表達(dá)式,但是Java開發(fā)者沒有選擇挫掏,只能使用匿名內(nèi)部類代替Lambda表達(dá)式侦另。
1. 語法
lambda 表達(dá)式的語法格式如下:
(parameters) -> expression
或
(parameters) ->{ statements; }
在最簡單的形式中,一個(gè)lambda可以由:用逗號(hào)分隔的參數(shù)列表
、–>符號(hào)
褒傅、函數(shù)體
三部分表示
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
如果Lambda表達(dá)式需要更復(fù)雜的語句塊弃锐,則可以使用花括號(hào)將該語句塊括起來,類似于Java中的函數(shù)體
Arrays.asList( "a", "b", "d" ).forEach( e -> {
System.out.print( e );
System.out.print( e );
} );
2. 以下是lambda表達(dá)式的重要特征:
- 可選類型聲明:不需要聲明參數(shù)類型殿托,編譯器可以統(tǒng)一識(shí)別參數(shù)值霹菊。
- 可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無需定義圓括號(hào),但多個(gè)參數(shù)需要定義圓括號(hào)支竹。
- 可選的大括號(hào):如果主體包含了一個(gè)語句旋廷,就不需要使用大括號(hào)。
- 可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值則編譯器會(huì)自動(dòng)返回值礼搁,大括號(hào)需要指定明表達(dá)式返回了一個(gè)數(shù)值柳洋。
3. Lambda 表達(dá)式的簡單例子:
// 1. 不需要參數(shù),返回值為 5
() -> 5
// 2. 接收一個(gè)參數(shù)(數(shù)字類型),返回其2倍的值
x -> 2 * x
// 3. 接受2個(gè)參數(shù)(數(shù)字),并返回他們的差值
(x, y) -> x – y
// 4. 接收2個(gè)int型整數(shù),返回他們的和
(int x, int y) -> x + y
// 5. 接受一個(gè) string 對(duì)象,并在控制臺(tái)打印,不返回任何值(看起來像是返回void)
(String s) -> System.out.print(s)
4. demo
使用 Lambda 表達(dá)式需要注意以下兩點(diǎn):
Lambda 表達(dá)式主要用來定義行內(nèi)執(zhí)行的方法類型接口,例如叹坦,一個(gè)簡單方法接口熊镣。在下面例子中,我們使用各種類型的Lambda表達(dá)式來定義MathOperation接口的方法募书。然后我們定義了sayMessage的執(zhí)行绪囱。
Lambda 表達(dá)式免去了使用匿名方法的麻煩,并且給予Java簡單但是強(qiáng)大的函數(shù)化的編程能力莹捡。
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
// 類型聲明
MathOperation addition = (int a, int b) -> a + b;
// 不用類型聲明
MathOperation subtraction = (a, b) -> a - b;
// 大括號(hào)中的返回語句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 沒有大括號(hào)及返回語句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括號(hào)
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括號(hào)
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
執(zhí)行以上腳本鬼吵,輸出結(jié)果為:
$ javac Java8Tester.java
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google
5. 變量作用域
接口變量必須是
public
、static
篮赢、final
修飾的
lambda 表達(dá)式只能引用標(biāo)記了 final 的外層局部變量齿椅,這就是說不能在 lambda 內(nèi)部修改定義在域外的局部變量,否則會(huì)編譯錯(cuò)誤启泣。
在 Java8Tester.java 文件輸入以下代碼:
public class Java8Tester {
final static String salutation = "Hello! ";
public static void main(String args[]){
GreetingService greetService1 = message ->
System.out.println(salutation + message);
greetService1.sayMessage("Runoob");
}
interface GreetingService {
void sayMessage(String message);
}
}
執(zhí)行以上腳本涣脚,輸出結(jié)果為:
$ javac Java8Tester.java
$ java Java8Tester
Hello! Runoob
我們也可以直接在 lambda 表達(dá)式中訪問外層的局部變量:
public class Java8Tester {
public static void main(String args[]) {
final int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2); // 輸出結(jié)果為 3
}
public interface Converter<T1, T2> {
void convert(int i);
}
}
lambda 表達(dá)式的局部變量可以不用聲明為 final,但是必須不可被后面的代碼修改(即隱性的具有 final 的語義)
int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;
//報(bào)錯(cuò)信息:Local variable num defined in an enclosing scope must be final or effectively final
在 Lambda 表達(dá)式當(dāng)中不允許聲明一個(gè)與局部變量同名的參數(shù)或者局部變量寥茫。
String first = "";
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //編譯會(huì)出錯(cuò)
二遣蚀、#函數(shù)式接口
Lambda的設(shè)計(jì)者們?yōu)榱俗尙F(xiàn)有的功能與Lambda表達(dá)式良好兼容,考慮了很多方法纱耻,于是產(chǎn)生了函數(shù)接口這個(gè)概念芭梯。函數(shù)接口指的是只有一個(gè)函數(shù)的接口,這樣的接口可以隱式轉(zhuǎn)換為Lambda表達(dá)式弄喘。
使用lambda表達(dá)式玖喘,新建一個(gè)類的時(shí)候,必須滿足以下三點(diǎn):
- 新建的類必須是接口蘑志,此接口稱為函數(shù)式接口
- 此接口只能有一個(gè)非抽象類接口累奈,但是可以使用default參數(shù)添加擴(kuò)展方法
- 忽略object類方法
1. 什么是函數(shù)式接口
所謂的函數(shù)式接口贬派,當(dāng)然首先是一個(gè)接口,然后就是在這個(gè)接口里面只能有一個(gè)抽象方法费尽。
這種類型的接口也稱為SAM
接口,即Single Abstract Method interfaces
2. 測(cè)試
- 準(zhǔn)備一個(gè)接口
public interface MyTest {
void test1();
}
- 使用lambda表達(dá)式實(shí)現(xiàn)該接口
public static void main(String[] args) {
MyTest myTest = () -> System.out.println("hello world!");
myTest.test1();
}
得到輸出為:hello world!
3. 如果接口類里面有多個(gè)方法行不行呢羊始?--- 可以
默認(rèn)方法和靜態(tài)方法不會(huì)破壞函數(shù)式接口的定義
- 在接口中加入一個(gè)方法
public interface MyTest {
void test1();
void test2();
}
會(huì)看到報(bào)如下錯(cuò)誤
Multiple non-overriding abstract methods found in interface
--在接口中找到多個(gè)非重寫抽象方法
找到重點(diǎn)詞多個(gè)抽象方法旱幼,那就是需要將抽象方法減少嘍,使用default
關(guān)鍵字突委,改造代碼如下:
public interface MyTest {
void test1();
default void test2() {
System.out.println("Bye bye world!");
}
}
再次調(diào)用
public static void main(String[] args) {
MyTest myTest = () -> System.out.println("hello world!");
myTest.test1();
myTest.test2();
}
out:
hello world!
Bye bye world!
4. 如何聲明一個(gè)接口為函數(shù)式接口
在接口上使用@FunctionalInterface
柏卤,這樣會(huì)在接口不符合函數(shù)式接口定義規(guī)范的時(shí)候就報(bào)錯(cuò)。
@FunctionalInterface
public interface Functional {
void method();
}