⭐ 推荐:社区规则条款 V1.0

Java 模式匹配 switch 怎么用?

满月居于空
满月居于空 版主萌新站长
发布于 2026-10-16 13:32 ·3 浏览 ·0 回复
内容摘要

Java 模式匹配 switch 自 JDK 21 正式定型,JDK 17–20 需加 --enable-preview;用 case 类型 变量名 -> 配合 when 守卫、case null 和密封类型穷尽检查,可替代 instanc

结论:Java 的模式匹配 switch 从 JDK 21(2023 年 9 月发布,JEP 441)起正式定型,JDK 17/18/19/20 只能加 --enable-preview 预览使用。核心用法是 case 类型 变量名 ->,配合 when 守卫、case null、密封类型的穷尽性检查,基本可以替代一长串 if (x instanceof ...) 的写法。

switch 模式匹配从哪个版本开始能用?

正式可用是 JDK 21。用 JDK 21 时直接写就行,编译命令是 javac --release 21 Demo.java;如果只有 JDK 17 到 20,需要额外加 javac --enable-preview --release 17 Demo.java,运行时也要 java --enable-preview Demo,否则报 Preview features are not enabled。

语法演化路径是:JEP 406(JDK 17 首次预览)→ JEP 420 → JEP 427 → JEP 433 → JEP 441(JDK 21 转正)。配套的记录模式解构(case Point(int x, int y))由 JEP 440 在同版本转正,两者经常一起用。

基本语法怎么写?

结论:模式 switch 用箭头 -> 写分支,每个 case 后跟类型模式或常量,自上而下匹配,命中一个就停。

static String describe(Object obj) {
    return switch (obj) {
        case Integer i when i > 100 -> "大于 100 的整数: " + i;
        case Integer i              -> "整数: " + i;
        case String s               -> "字符串: " + s;
        case null                   -> "null";
        default                     -> "其它类型";
    };
}

三条硬规则:第一,绑定变量 i、s 的作用域只在箭头右侧,出不了这个分支;第二,类型模式和常量模式可以混着写,case Integer i 和 case 0 能共存;第三,顺序敏感,子类型必须写在父类型前面,把 case Object o 放在 case String s 前面会直接编译失败,报错信息是 this case label is dominated by a preceding case label。

什么时候可以不写 default?

结论:当 switch 的选择器是密封类/密封接口,且所有 permits 子类型都被覆盖时,编译器认定已经穷尽,可以不写 default;enum 覆盖全部常量时同理。其它类型(比如 Object)必须写 default,否则报 the switch expression does not cover all possible input values。

注意:使用了类型模式的 switch 语句(不只是表达式)同样要求穷尽。这点和传统的 switch (int) 不一样,老写法可以只写几个分支,新模式写不全直接编译不过。

sealed interface Shape permits Circle, Rect {}
record Circle(double r) implements Shape {}
record Rect(double w, double h) implements Shape {}

static double area(Shape s) {
    return switch (s) {
        case Circle(double r)  -> Math.PI * r * r;
        case Rect(double w, double h) -> w * h;
    }; // 无需 default
}

case null 有什么用?

结论:传统 switch 传 null 直接抛 NullPointerException,模式 switch 可以用 case null -> 显式接住它。如果想让 null 走默认逻辑,可以合并写成 case null, default -> ...。

要留意:穷尽性检查不包含 null。一个密封类型的 switch 就算覆盖了全部子类,运行时传入 null 仍然抛 NPE——所以生产代码里建议显式加一个 case null。

when 守卫怎么写?

结论:在模式后面加 when 布尔表达式 做二次筛选,只有条件为 true 才走该分支。

when 是 JDK 21 新增的上下文关键字(JEP 441 前用的是 && 和 case ... &&,预览期改过,正式版统一为 when)。守卫里的表达式可以引用刚绑定的变量,比如 case String s when s.isEmpty() -> "空串"。守卫不改变穷尽性判断的复杂度,写多了也不会影响编译器静态检查。

记录模式怎么解构?

结论:case Point(int x, int y) 直接把 record 的组件拆成局部变量,支持任意层嵌套,无需再调 x()、y()。

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

static String fmt(Object o) {
    return switch (o) {
        case Line(Point(int x1, int y1), Point(int x2, int y2)) ->
            "从 (%d,%d) 到 (%d,%d)".formatted(x1, y1, x2, y2);
        case Point(int x, int y) -> "点 (%d,%d)".formatted(x, y);
        default -> "未知";
    };
}

有哪些坑要注意?

第一,泛型擦除:case List<String> l 编译不过,只能写 case List<?> l,再靠守卫判断元素类型。第二,同一个类型的模式不能重复出现,编译器会报 dominated 错误。第三,升级到 JDK 21 后,老代码里如果有变量名叫 when,在 case 标签位置可能被当成关键字解析,改个名即可。第四,预览期语法变动过,网上 JDK 17 时代带 && 的示例在 JDK 21 上编译失败,以官方最终语法为准。

一句话收束:JDK 21 起,case 类型 变量 -> + when 守卫 + case null + 密封类穷尽性,就是模式匹配 switch 的标准写法;JDK 17 到 20 记得开 --enable-preview,同时避开泛型擦除和支配顺序这两个编译期陷阱。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2184.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 0

还没有回复,来抢沙发~