Java Pattern Matching
Pattern Matching(模式匹配)是 Java 從 14 版開始逐步引入的功能,讓類型檢查和轉型更加簡潔。
instanceof Pattern Matching (Java 16+)
傳統寫法
// 舊寫法:需要額外的型別轉換
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
Pattern Matching 寫法
// 新寫法:直接綁定變數
if (obj instanceof String s) {
System.out.println(s.length());
}
// 可以在同一行使用
if (obj instanceof String s && s.length() > 5) {
System.out.println("長字串: " + s);
}
作用域規則
// 變數在 pattern 匹配成功後的作用域內可用
if (obj instanceof String s) {
// s 在這裡可用
System.out.println(s);
}
// s 在這裡不可用
// 否定條件時,變數在 else 區塊可用
if (!(obj instanceof String s)) {
return;
}
// s 在這裡可用(因為如果不是 String 就已經 return 了)
System.out.println(s.length());
Switch Pattern Matching (Java 21+)
類型模式
static String formatValue(Object obj) {
return switch (obj) {
case Integer i -> "整數: " + i;
case Long l -> "長整數: " + l;
case Double d -> "浮點數: " + d;
case String s -> "字串: " + s;
case null -> "空值";
default -> "未知類型: " + obj.getClass();
};
}
Guard 條件(when)
static String classify(Object obj) {
return switch (obj) {
case Integer i when i < 0 -> "負數";
case Integer i when i == 0 -> "零";
case Integer i when i > 0 -> "正數";
case String s when s.isEmpty() -> "空字串";
case String s when s.length() < 5 -> "短字串";
case String s -> "長字串";
case null -> "空值";
default -> "其他";
};
}
null 處理
static String process(String s) {
return switch (s) {
case null -> "空值";
case String str when str.isEmpty() -> "空字串";
case String str -> "內容: " + str;
};
}
Record Pattern (Java 21+)
基本解構
record Point(int x, int y) {}
static void printPoint(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println("x = " + x + ", y = " + y);
}
}
巢狀解構
record Point(int x, int y) {}
record Line(Point start, Point end) {}
static void printLine(Object obj) {
if (obj instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {
System.out.println("從 (" + x1 + "," + y1 + ") 到 (" + x2 + "," + y2 + ")");
}
}
搭配 Switch
record Circle(double radius) {}
record Rectangle(double width, double height) {}
record Triangle(double base, double height) {}
static double area(Object shape) {
return switch (shape) {
case Circle(double r) -> Math.PI * r * r;
case Rectangle(double w, double h) -> w * h;
case Triangle(double b, double h) -> 0.5 * b * h;
default -> 0;
};
}
搭配 Sealed Classes
sealed interface Shape permits Circle, Rectangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
static double calculateArea(Shape shape) {
return switch (shape) {
case Circle(double r) -> Math.PI * r * r;
case Rectangle(double w, double h) -> w * h;
// 不需要 default,編譯器知道所有可能
};
}
實用範例
JSON 節點處理
sealed interface JsonNode permits JsonNull, JsonBool, JsonNumber, JsonString, JsonArray, JsonObject {}
record JsonNull() implements JsonNode {}
record JsonBool(boolean value) implements JsonNode {}
record JsonNumber(double value) implements JsonNode {}
record JsonString(String value) implements JsonNode {}
record JsonArray(List<JsonNode> elements) implements JsonNode {}
record JsonObject(Map<String, JsonNode> fields) implements JsonNode {}
static String stringify(JsonNode node) {
return switch (node) {
case JsonNull() -> "null";
case JsonBool(boolean b) -> String.valueOf(b);
case JsonNumber(double n) -> String.valueOf(n);
case JsonString(String s) -> "\"" + s + "\"";
case JsonArray(List<JsonNode> arr) ->
arr.stream().map(Main::stringify).collect(Collectors.joining(", ", "[", "]"));
case JsonObject(Map<String, JsonNode> obj) ->
obj.entrySet().stream()
.map(e -> "\"" + e.getKey() + "\": " + stringify(e.getValue()))
.collect(Collectors.joining(", ", "{", "}"));
};
}
表達式計算
sealed interface Expr permits Num, Add, Mul, Neg {}
record Num(int value) implements Expr {}
record Add(Expr left, Expr right) implements Expr {}
record Mul(Expr left, Expr right) implements Expr {}
record Neg(Expr operand) implements Expr {}
static int eval(Expr expr) {
return switch (expr) {
case Num(int n) -> n;
case Add(Expr l, Expr r) -> eval(l) + eval(r);
case Mul(Expr l, Expr r) -> eval(l) * eval(r);
case Neg(Expr e) -> -eval(e);
};
}
// -(2 + 3) * 4 = -20
Expr expr = new Mul(new Neg(new Add(new Num(2), new Num(3))), new Num(4));
System.out.println(eval(expr)); // -20
訊息處理
sealed interface Message permits TextMessage, ImageMessage, FileMessage {}
record TextMessage(String sender, String content) implements Message {}
record ImageMessage(String sender, String url, int width, int height) implements Message {}
record FileMessage(String sender, String filename, long size) implements Message {}
static void handleMessage(Message msg) {
switch (msg) {
case TextMessage(String sender, String content) ->
System.out.println(sender + " 說: " + content);
case ImageMessage(String sender, String url, int w, int h) ->
System.out.println(sender + " 分享了圖片 " + w + "x" + h);
case FileMessage(String sender, String name, long size) ->
System.out.println(sender + " 分享了檔案 " + name + " (" + size + " bytes)");
}
}
版本歷史
| 版本 | 功能 |
|---|---|
| Java 14 | instanceof Pattern Matching (Preview) |
| Java 16 | instanceof Pattern Matching (正式) |
| Java 17 | Switch Pattern Matching (Preview) |
| Java 21 | Switch Pattern Matching (正式) |
| Java 21 | Record Pattern (正式) |
重點整理
instanceofpattern matching 省去手動型別轉換- Switch pattern matching 支援類型模式和 null 處理
when關鍵字可加入額外條件- Record pattern 可以解構 record 的組成
- 搭配 sealed classes 實現窮舉檢查
- 大幅簡化類型判斷和資料處理的程式碼