Skip to content

Pattern Matching

模式匹配让你写更少的代码,做更少的事。

JDK 16 正式引入了 instanceof 的模式匹配,JDK 17 增强了 switch 的模式匹配。

instanceof 的模式匹配

以前 vs 现在

java
Object obj = "Hello";

// 以前:需要强制转换
if (obj instanceof String) {
    String s = (String) obj;  // 多余的代码
    System.out.println(s.length());
}

// 现在:一步到位
if (obj instanceof String s) {
    System.out.println(s.length());  // s 自动是 String 类型
}

作用域规则

模式变量 s 只在 true 分支有效:

java
if (!(obj instanceof String s)) {
    // 这里 s 不可用
    System.out.println("not a string");
} else {
    // 这里 s 可用
    System.out.println(s.length());
}

// 或者
if (obj instanceof String s) {
    // 这里 s 可用
    System.out.println(s.length());
}
// 这里 s 不可用

结合否定

java
if (!(obj instanceof String s)) {
    // obj 不是 String
    return;
}
// obj 是 String,s 在这里可用
System.out.println(s.length());

switch 的模式匹配(JDK 21)

基本语法

java
// JDK 21+
Object obj = 42;

String result = switch (obj) {
    case Integer i -> "整数: " + i;
    case String s -> "字符串: " + s;
    case null -> "null";
    default -> "其他";
};

带条件的模式

java
String describe(Object obj) {
    return switch (obj) {
        case Integer i when i > 0 -> "正整数: " + i;
        case Integer i -> "整数: " + i;
        case String s when s.length() > 5 -> "长字符串: " + s;
        case String s -> "字符串: " + s;
        case null -> "null";
        default -> "其他: " + obj.getClass().getSimpleName();
    };
}

when 子句叫模式守卫(Pattern Guard)

Record 模式

java
record Point(int x, int y) {}
record Line(Point start, Point end) {}

// 嵌套解构
void printLine(Line line) {
    String desc = switch (line) {
        case Line(Point(int x1, int y1), Point(int x2, int y2)) ->
            "线段从(" + x1 + "," + y1 + ")到(" + x2 + "," + y2 + ")";
    };
    System.out.println(desc);
}

完整示例

场景一:类型安全的计算器

java
public class Calculator {

    public static Expr create(double value) {
        return new Const(value);
    }

    public static Expr create(String op, Expr left, Expr right) {
        return switch (op) {
            case "+" -> new Add(left, right);
            case "-" -> new Sub(left, right);
            case "*" -> new Mul(left, right);
            case "/" -> new Div(left, right);
            default -> throw new IllegalArgumentException("Unknown op: " + op);
        };
    }

    public static double eval(Expr expr) {
        return switch (expr) {
            case Const c -> c.value();
            case Add a -> eval(a.left()) + eval(a.right());
            case Sub s -> eval(s.left()) - eval(s.right());
            case Mul m -> eval(m.left()) * eval(m.right());
            case Div d -> eval(d.left()) / eval(d.right());
        };
    }

    // 表达式接口和实现
    public sealed interface Expr permits Const, Add, Sub, Mul, Div {}
    public record Const(double value) implements Expr {}
    public record Add(Expr left, Expr right) implements Expr {}
    public record Sub(Expr left, Expr right) implements Expr {}
    public record Mul(Expr left, Expr right) implements Expr {}
    public record Div(Expr left, Expr right) implements Expr {}

    public static void main(String[] args) {
        // (2 + 3) * 4 = 20
        Expr expr = create("*",
            create("+", create(2), create(3)),
            create(4)
        );
        System.out.println(eval(expr));  // 20.0
    }
}

场景二:JSON 解析

java
public class JsonDemo {

    public static String describe(Object json) {
        return switch (json) {
            case null -> "null";
            case Boolean b -> "布尔: " + b;
            case Number n -> "数字: " + n;
            case String s -> "字符串: " + s;
            case List<?> list -> "数组,长度=" + list.size();
            case Map<?, ?> map -> "对象,键=" + map.size();
            default -> "未知类型";
        };
    }

    public static void main(String[] args) {
        System.out.println(describe("hello"));        // 字符串: hello
        System.out.println(describe(42));              // 数字: 42
        System.out.println(describe(List.of(1, 2)));   // 数组,长度=2
    }
}

与 Sealed Classes 配合

模式匹配和密封类是天生一对:

java
public sealed interface Shape permits Circle, Rectangle, Square {}

public record Circle(double radius) implements Shape {}
public record Rectangle(double width, double height) implements Shape {}
public record Square(double side) implements Shape {}

// 编译器确保穷尽
public double area(Shape shape) {
    return switch (shape) {
        case Circle c -> Math.PI * c.radius() * c.radius();
        case Rectangle r -> r.width() * r.height();
        case Square s -> s.side() * s.side();
    };
}

常用模式

类型模式

java
if (obj instanceof String s) {
    // s 是 String
}

记录模式

java
if (obj instanceof Point(int x, int y)) {
    System.out.println(x + y);
}

数组模式

java
if (obj instanceof String[] arr && arr.length > 0) {
    System.out.println(arr[0]);
}

逻辑组合

java
if (obj instanceof String s && !s.isBlank()) {
    // s 非空字符串
}

小结

模式匹配让代码更简洁:

java
// ❌ 以前
if (obj instanceof String) {
    String s = (String) obj;
    // 使用 s
}

// ✅ 现在
if (obj instanceof String s) {
    // 直接使用 s
}

// ✅ JDK 21+ switch
return switch (obj) {
    case Integer i -> "整数: " + i;
    case String s -> "字符串: " + s;
    case null -> "null";
    default -> "其他";
};

结合 Sealed Classes 使用,编译器能确保穷尽性,代码既简洁又安全。

基于 VitePress 构建