JDK JDK 学习手册8 → 27 · JDK 专用学习站
QUICKREF · LANGUAGE · JEP 361

switch 表达式:让分支返回值

JDK 17 · 速查主题 04 / 06 · 预计 15 分钟
基础15 分钟JEP 361
一句话switch 从语句变成表达式后,分支可以直接产出值,箭头标签取消贯穿,编译器还会检查穷尽性。它最适合 enum、有限集合和纯映射逻辑;复杂控制流仍应留在语句里。

学完你要能自己回答

  1. switch 表达式什么时候必须穷尽?enum 与 String 的规则有什么不同?
  2. yield 和 return 在 switch 表达式里怎么区分?
  3. 选择器为 null 时会发生什么?17 能写 case null 吗?

从语句到表达式01

switch 表达式整体产出一个值,可以直接赋给变量、作为 return 的参数。箭头标签 -> 不会贯穿,每个分支只执行自己的代码。

enum Op { ADD, SUB }

static int apply(Op op, int a, int b) {
    return switch (op) {
        case ADD -> a + b;
        case SUB -> {
            int r = a - b;
            yield r;
        }
    };
}

System.out.println(apply(Op.ADD, 2, 3));
System.out.println(apply(Op.SUB, 2, 3));
5
-1
  • 箭头标签不会 fall through,不需要 break。
  • 分支可以是表达式,也可以是代码块;代码块用 yield 返回值。
  • switch 表达式必须穷尽;enum 覆盖所有常量时可以省略 default。
  • return 在 switch 表达式里表示「从方法返回」,不是「从这个分支产出值」。
箭头标签不是「更短的 case」。它同时改变了控制流语义:没有贯穿、没有隐式落空,分支边界更清晰。

yield:代码块里的返回值02

当分支需要多行逻辑时,用 {} 包住,并在末尾 yield 一个值。yield 只能出现在 switch 表达式的分支块里;switch 语句里不能用它产出值。

static String grade(int score) {
    return switch (score / 10) {
        case 10, 9 -> "A";
        case 8 -> "B";
        case 7 -> {
            String note = score >= 75 ? "B+" : "C";
            yield note;
        }
        default -> "D";
    };
}
写法JDK 17 行为
case X -> value;分支直接产出 value
case X -> { ... yield v; }代码块用 yield 产出 v
case X: ... break;传统语句写法,不能作为表达式值
return v; 在分支里从整个方法返回,不是产出分支值
在 switch 表达式里用 break 带值(break v;)是早期预览语法,14 正式版已经改为 yield。看到旧资料要注意版本。

穷尽性:编译器替你查漏03

switch 表达式必须覆盖所有可能值。对 enum,覆盖所有常量即可省略 default;对 String、int 等开放类型,必须提供 default。

enum Level { LOW, HIGH }

static int weight(Level level) {
    return switch (level) {
        case LOW -> 1;
        case HIGH -> 2;
    };
}

这段代码没有 default,仍能编译。以后给 Level 增加 MEDIUM,所有这样的 switch 会立即编译失败——这是 enum 穷尽性最有价值的地方。

  • enum 的穷尽性按常量名检查;新增常量会破坏所有未写 default 的 switch 表达式。
  • String / int 的 switch 表达式没有封闭集合,必须写 default。
  • sealed 类型的穷尽性在 17 仍依赖预览的 switch 类型模式;普通 switch 不能按子类型穷尽。
  • switch 语句不要求穷尽;switch 表达式要求穷尽,这是两者最重要的区别之一。
如果确实想兜底未来新增的 enum 常量,写 default;但要想清楚:你是想现在编译失败,还是想运行时悄悄走默认分支。

null 选择器04

普通 switch(语句或表达式)在选择器为 null 时抛 NullPointerException。17 的 case null 只在 JEP 406 预览的 switch 类型模式里可用,普通 switch 不能写。

static int code(String s) {
    return switch (s) {
        case "a" -> 1;
        default -> 0;
    };
}

try {
    System.out.println(code(null));
} catch (NullPointerException e) {
    System.out.println("null-npe");
}
null-npe

如果业务上 null 有明确含义,应在 switch 之前显式判断,或使用 17 预览的 case null(并接受预览限制)。不要依赖 default 接住 null:它不会。

21 的 switch 模式匹配正式支持 case null 与 case null, default;17 只有预览版支持,不能作为稳定契约。跨版本迁移时,这段代码必须重新编译并调整。

从旧 switch 迁移05

旧写法新写法注意
每个 case 都 break箭头标签 ->不再需要 break,也不会贯穿
先赋临时变量再 returnswitch 表达式直接返回分支必须穷尽
代码块里给 result 赋值yieldyield 只在分支块内
default 抛异常兜底enum 覆盖所有常量新增常量时编译失败,更安全
嵌套 if 判断类型17 预览 switch 类型模式生产代码谨慎,需 --enable-preview
// JDK 8
int result;
switch (op) {
    case ADD:
        result = a + b;
        break;
    case SUB:
        result = a - b;
        break;
    default:
        throw new IllegalArgumentException();
}

// JDK 17
int result = switch (op) {
    case ADD -> a + b;
    case SUB -> a - b;
};
不是所有 switch 都值得改成表达式。分支里有副作用、需要贯穿、或逻辑很复杂时,保留 switch 语句更清楚;表达式的优势在「纯映射」。

版本边界BOUNDARY

这张表比 API 本身更值得记。写代码时先确认你的运行时版本。

特性 / API状态说明
switch 表达式14 正式JEP 361;JDK 12 / 13 预览。
yield14 正式替代预览期的 break value。
switch 类型模式17 预览JEP 406;需 --enable-preview,21 正式。
case null17 仅预览普通 switch 选择器为 null 仍抛 NPE。
sealed 穷尽 switch17 预览需要类型模式;sealed 本身不改变 switch 规则。
switch 表达式穷尽性14 正式enum 可省略 default;String / int 必须 default。

费曼自检SELF CHECK

先合上页面,用自己的话回答,再展开对照。能讲出因果和反例,才算真正掌握。

01. switch 表达式和 switch 语句最关键的区别是什么?
表达式必须产出一个值并且必须穷尽;语句不要求穷尽,也不产出值。表达式用箭头标签和 yield,语句仍可使用冒号标签与 break。
02. 什么时候可以省略 default?
只有选择器是 enum 且覆盖了所有常量时可以省略。String、int 等开放类型必须写 default,否则编译失败。
03. yield 和 return 有什么区别?
yield 只从 switch 表达式的分支块产出一个值,控制流继续留在方法内;return 直接从整个方法返回。分支块里误写 return 会改变方法行为。
04. 17 的 switch 遇到 null 会怎样?
普通 switch 抛 NullPointerException,default 不会接住 null。只有 17 预览的 switch 类型模式支持 case null;21 才正式。
学习进度 记录保存在本机浏览器 · 不上传

JDK 学习手册 · JDK / Java 专用学习站 · 纯静态 · 双击 index.html 即可使用

本站专注 JDK 版本演进与 Java 教学,非 Oracle / OpenJDK 官方站点。版本数据来自 OpenJDK 官方 Features 表;课程示例已在对应本机 JDK 实测。构建日期 2026-11-12。