簡介
Lambda 表達式是 Java 1.8 跟 Stream 機制一同推出的。Lambda 表達式極大地減少了代碼量,增加了代碼的可讀性。
引入 Lambda 表達式之后,Java 開始支持把函數作為參數傳遞。
前置條件
使用 Lambda 表達式的前置條件,作為參數的接口必須是函數式接口
- 首先類型必須是接口
interface
,不能是類class
。比如,抽象類就不可以。 - 函數式接口有且僅有一個未被覆寫的抽象方法
舉例:
- Object 中方法不算
// MyRunnable 仍然算是一個“函數式接口”
public interface MyRunnable extends Runnable {
String toString();
boolean equals(Object obj);
int hashCode();
}
- 接口中的 default 方法不算
// MyRunnable2 仍然算是一個“函數式接口”
public interface MyRunnable2 extends Runnable {
default void run2() {}
}
FunctionalInterface 注解
@FunctionalInterface
可以幫助我們在編譯期識別出一個接口是否是“函數式接口”:
參數的傳遞
假如我們有一個如下含義的“函數式接口”:
@FunctionalInterface
public interface Formatter {
void format(String name, int age);
}
我們可以構造一個測試:
public class LambdaTest {
public static void main(String[] args) {
print((String name, int age)-> System.out.println(String.format("name:%s age:%d", name, age)), "ziyu", 18);
}
public static void print(Formatter formatter, String name, int age) {
formatter.format(name, age);
}
}
多個參數
當有多個參數時,可以選擇省略所有參數類型聲明:
注意:
不能省略一部分保留一部分。(String name, age) -> System.out.println(name); 這是不合法的!
單個參數
當只有一個參數時,除了可以省略參數類型,還可以進一步省略掉括號。
編寫方式
沒有返回值
Runnable
就是一個常用的“函數式接口”,它的抽象方法 run()
“沒有返回值”, 剛好適合用於此處的演示。測試例子如下:
public class LambdaTest2 {
public static void main(String[] args) {
runIt(()->{
System.out.println("123");
});
}
static void runIt(Runnable runnable) {
new Thread(runnable).start();
}
}
- 如果寫成多行表達式,那么需要
{}
來表示代碼塊,且每一行代碼結束時需要書寫;
表示語句的結束。 - 如果代碼塊中只有一條語句,那么可以通過省略
{}
和;
來簡寫為單行表達式
有返回值
我們定義一個 IdFactory
接口來做演示:
public interface IdFactory {
String generateId();
}
我們的示例代碼如下:
import java.util.UUID;
public class LambdaTest3 {
public static void main(String[] args) {
String name = getId(()-> UUID.randomUUID() + "");
System.out.println(name);
}
static String getId(IdFactory factory) {
return factory.generateId();
}
}
- 如果寫成多行表達式,除了需要
{}
來表示代碼塊,和每一行代碼結束時需要書寫;
表示語句的結束以外,還應該在需要返回值的方法用return
來返回值。 - 如果代碼塊中只有一條語句,那么可以通過省略
{}
和;
以及return
來簡寫為單行表達式
方法引用
比如我們要寫一段代碼,用來打印出完整的加法表達式 a + b = c
,並且要求根據 a 和 b 求出 c 作為函數返回值。
首先我們定一個“計算器”接口:
public interface Calculator {
int compute(int a, int b);
}
接着我們寫一個測試用例:
public class LambdaTest3 {
public static void main(String[] args) {
int a = 10;
int b = 20;
// 這里不能用 (a, b), 那樣的話會產生歧義,使得編譯器報錯
runIt((x, y)->{
System.out.print(x);
System.out.print(" + ");
System.out.print(y);
System.out.print(" = ");
System.out.println(x + y);
return x + y;
}, a, b);
}
static void runIt(Calculator calculator, int a, int b) {
calculator.compute(a, b);
}
}
靜態方法引用
- 在 LambdaTest2 中定一個靜態方法
sum
- 使用靜態方法引用
LambdaTest2::sum
- 這樣在運行靜態方法
runIt
中的第一行calculator.compute(a, b)
時,a
和b
會通過靜態方法引用傳遞給靜態方法sum
通過這種方式,即簡化了參數的傳遞,也把“多行表達式”簡化為了“單行表達式”
成員方法引用
new LambdaTest2()::sum
: 先新建對象LambdaTest2
,並且通過該對象來使用成員變量引用
小貼士:
如果先聲明變量obj
,再使用obj::sum
也是合法的。
public static void main(String[] args) {
int a = 10;
int b = 20;
LambdaTest2 obj = new LambdaTest2();
runIt(obj::sum, a, b);
}
總結
- Lambda表達式的前置條件:必須是“函數式接口”
- 單個參數傳遞時,可以省略參數兩端的括號。參數的類型可以一起省略。
- 編寫的方式主要包括單行表達式和多行表達式
- 可以使用方法引用來把多行表達式寫成單行表達式。方法引用又包括了靜態方法引用和動態方法引用。