Java8之lambda表示式
阿新 • • 發佈:2020-07-14
簡介
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表示式的前置條件:必須是“函式式介面”
- 單個引數傳遞時,可以省略引數兩端的括號。引數的型別可以一起省略。
- 編寫的方式主要包括單行表示式和多行表示式
- 可以使用方法引用來把多行表示式寫成單行表示式。方法引用又包括了靜態方法引用和動態方法引用。