HOWTO · Java
Java 中的箭頭運算子 ->
這篇文章就是要了解 Java 中的箭頭運算子。
本頁內容
在 Java 中,-> 是 lambda 表示式的分隔符號,不是通用的計算運算子。它將 lambda 左側的參數與右側的主體分開。Lambda 會為相容的函式介面提供實作;函式介面是只有一個抽象方法的介面。Lambda 在 Java 8 中引入,能讓某些實作更精簡,但不能取代所有匿名類別,也不保證程式碼一定更快或更清楚。
Java 中箭頭運算子的意義
在 Java 8 之前,可以用包含 run 方法的匿名類別來撰寫 Runnable 工作。Lambda 可以用 () -> System.out.println("Working") 表達相同行為。箭頭仍屬於 lambda 表示式的一部分;它不會建立不依賴目標型別的獨立函式值。編譯器會根據變數宣告或方法引數等周遭情境,判斷目標函式介面的型別。
Lambda 表示式的語法與規則
Lambda 由參數清單、箭頭和主體組成。表達式主體可以省略大括號,例如 x -> x * x;區塊主體則使用大括號,而且當目標方法有回傳值時,必須明確寫出 return。沒有參數的 lambda 使用 ();多個參數則以括號括起來,例如 (left, right) -> left + right。單一參數也可以加上括號,例如 (name) -> name.length()。
若區塊主體的 lambda 會回傳值,目標方法的回傳型別會決定 lambda 的結果型別。以下範例將 lambda 指派給 ToIntFunction<String>,其 applyAsInt 方法會回傳 int:
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import java.util.function.ToIntFunction;
public class BlockLambdaExample {
public static void main(String[] args) {
ToIntFunction<String> length = (String name) -> {
return name.length();
};
System.out.println(length.applyAsInt("Mira"));
}
}
主體必須符合目標方法的回傳型別與受檢例外規則。Lambda 不能指派給任意類別,也不能指派給具有多個彼此無關之抽象方法的介面。依照這項規則,介面的預設方法與靜態方法不會算作額外的抽象方法。
使用 Lambda 搭配函式介面
Lambda 可以指派給簡單的函式介面,再透過該介面的方法呼叫。這個獨立範例先列出包含 main 的類別,因此可以直接執行。其輸出是 draw() 印出的那一行。
執行此範例:程式碼與輸出中的文字標籤維持英文,與不可變更的範例內容一致。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
public class ArrowExample {
@FunctionalInterface
interface Drawable {
void draw();
}
public static void main(String[] args) {
Drawable shape = () -> System.out.println("Drawing a circle");
shape.draw();
}
}
Drawing a circle
Java 集合、串流與執行緒中的常見用途
forEach 等集合方法會接受函式介面,因此可以用 lambda 描述要對每個元素執行的動作。在此範例中,name 代表清單目前的元素,而 Consumer<String> lambda 會將它印出。範例中的名稱維持英文。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import java.util.Arrays;
import java.util.List;
public class CollectionLambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Ava", "Mira");
names.forEach(name -> System.out.println(name));
}
}
串流的 filter 方法會接收 Predicate<String> lambda,以保留符合條件的元素。接著的 forEach 操作會印出通過述詞檢查的每個名稱。範例中的名稱維持英文:
import java.util.Arrays;
import java.util.List;
public class StreamLambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Ava", "Mira", "Bo");
names.stream()
.filter(name -> name.length() > 2)
.forEach(name -> System.out.println(name));
}
}
這些 API 提供不同的迭代與處理方式;如果傳統迴圈更容易呈現控制流程,仍可能是較合適的選擇。
Runnable 常用來表示執行緒要執行的工作,因為它唯一的抽象方法不接收引數,也不回傳值。此 lambda 提供 Runnable 工作;建立 Thread 並不會執行工作,呼叫 start() 才會安排它獨立執行。範例中的輸出文字維持英文:
public class ThreadLambdaExample {
public static void main(String[] args) {
Runnable task = () -> System.out.println("Task running");
Thread worker = new Thread(task);
worker.start();
}
}
執行緒的輸出相對於其他並行工作,可能以不同順序出現;排程也沒有保證。當目標介面和周遭程式碼能清楚表達行為時,再使用 lambda。它能減少語法,但不代表可以忽略 API 或並行行為。