!!! info "版本信息" - 最低 JDK:21(switch 模式匹配 17–20 预览 → 21 正式;record 解构 19–20 预览 → 21 正式) - JEP:JEP 441: Pattern Matching for switch | JEP 440: Record Patterns
switch 表达式(JDK 14)只能按常量分支。要在 switch 里「按对象类型」分发,JDK 17 之前只能堆 if-else + instanceof。JEP 441 把模式匹配搬进 switch:可以 case Integer i、case String s,还能 case null、用 when 守卫细化。JEP 440 再补上 record 解构:case Circle(double r) 把记录的组件直接拆出来。配合 sealed,编译器能判定穷尽性——分支覆盖所有可能时无需 default。
先把模型建好——一个 sealed 接口 + 三个 record(代数数据类型的三件套:sealed + record + pattern matching):
--8<-- "examples/pattern-switch/src/main/java/com/javamodern/pmswitch/Shape.java"然后是 examples/pattern-switch/ 里真实可运行的 switch 逻辑(snippets 嵌入,与示例零漂移):
--8<-- "examples/pattern-switch/src/main/java/com/javamodern/pmswitch/PatternSwitchDemo.java"- :material-lightbulb:
area按 record 类型匹配并解构组件;Shape是 sealed,三个实现者全覆盖 → 无需default。classify展示case null与when守卫。
运行 PatternSwitchDemo.main 的实测输出(JDK 21):
area(Circle(2)) = 12.566370614359172
area(Square(3)) = 9.0
area(Rectangle(3,4)) = 12.0
空
负整数
整数 7
字符串 hi
=== "JDK 8(if-else + instanceof + 强转)"
java if (shape instanceof Circle) { double r = ((Circle) shape).radius(); return Math.PI * r * r; } else if (shape instanceof Square) { double s = ((Square) shape).side(); return s * s; } else if (shape instanceof Rectangle) { Rectangle r = (Rectangle) shape; return r.width() * r.height(); } else { throw new IllegalArgumentException("未知形状"); // 老是兜底,编译器帮不上忙 }
=== "JDK 21(switch 按类型 + record 解构)"
java return switch (shape) { // sealed 穷尽,无 default case Circle(double r) -> Math.PI * r * r; case Square(double s) -> s * s; case Rectangle(double w, double h) -> w * h; };
- 类型模式 + 解构:
case Circle(double r)是「类型模式 + record 模式」的嵌套——先匹配类型,再把组件radius绑给r,r在箭头右侧可直接用。 - 穷尽性(exhaustiveness):switch 表达式必须覆盖所有可能值。
Shape是 sealed,编译器知道实现者恰好是Circle/Square/Rectangle,三者齐全即穷尽;漏一个会编译报错(不是运行时兜底)。非 sealed/开放的类型则需default。 case null与when守卫:JEP 441 允许显式case null(旧 switch 对null直接 NPE);case Integer i when i < 0用when在模式之上加布尔条件,多个同类型分支按顺序匹配。
graph TD
S["switch (shape)"] --> C["case Circle(double r)"]
S --> Q["case Square(double s)"]
S --> R["case Rectangle(double w, h)"]
E["sealed permits Circle,Square,Rectangle"] -.编译器据此判穷尽.- S
X["漏写任一分支"] -.编译报错.- S
style S fill:#bbdefb
style X fill:#ffcdd2
style E fill:#c8e6c9
when守卫要按顺序:case Integer i when i < 0必须在case Integer i之前,否则永不被命中(无守卫的分支先吃掉)。- 非 sealed 类型仍要
default:只有 switch 的选择器类型被sealed限定、或被 record 全覆盖时才能省default;普通Object必须有default(如本例classify)。 case null不是默认:旧switch遇null直接抛 NPE;新模式下,不写case null时null仍会走default或抛 NPE——要显式case null才优雅处理。- 与
instanceof模式匹配(JDK 16)是递进关系:单分支用instanceof,多分支用switch模式匹配;record 解构让「拆字段」也一步到位。
switch 模式匹配 + record 解构把「按类型分发、按结构取值」两件事在 switch 里一次完成。与 sealed 的穷尽性检查合在一起,JDK 21 终于让 Java 拥有了顺手的代数数据类型写法——if-else+强转+兜底的旧时代画上句号。