Switch 表达式

在 Java SE 14 中,您可以使用另一种更便捷的 switch 关键字语法:switch 表达式

促成这种新语法的设计有以下几个因素。

  1. 默认情况下,switch 标签之间的控制流行为是继续执行后续语句。这种语法容易出错,并导致应用程序出现缺陷。
  2. switch 表达式被视为一个整体块。当您需要仅在特定情况下定义变量时,这可能构成障碍。
  3. switch 语句是一种语句。在前几节的示例中,每个分支都为变量赋值。将其改为表达式可能使代码更优雅且更易于阅读。

上一节介绍的语法结构称为switch语句,在Java SE 14中仍然可用,其语义未发生改变。从Java SE 14开始,switch语句新增了一种语法形式:switch 表达式。
此语法修改了switch标签的语法。假设您的应用程序中有以下switch语句:

Day day = ...; // any day
int len = 0;
switch(day){
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        len = 6;
        break;
    case TUESDAY:
        len = 7;
        break;
    case THURSDAY:
    case SATURDAY:
        len = 8;
        break;
    case WEDNESDAY:
        len = 9;
        break;
}
IO.println("len = " + len);

使用 switch 表达式语法,现在可以按以下方式编写代码。

Day day = ...; // any day
int len = 
    switch (day){
        case MONDAY, FRIDAY, SUNDAY -> 6;
        case TUESDAY                -> 7;
        case THURSDAY, SATURDAY     -> 8;
        case WEDNESDAY              -> 9;
    };
IO.println("len = " + len);

switch标签的语法现为 case L ->。仅当标签匹配时,才会执行标签右侧的代码。该代码可以是单个表达式、代码块或throw语句。由于这段代码构成一个独立代码块,您可以在其中定义仅作用于该代码块的局部变量。

此语法还支持每个case包含多个常量,用逗号分隔,如前例所示。

生成一个值

此 switch 语句可作为表达式使用。例如,前一节的示例可通过 switch 语句重写如下:

int quarter =  ...; // any value
String quarterLabel = 
    switch (quarter) {
        case 0 -> "Q1 - Winter";
        case 1 -> "Q2 - Spring";
        case 2 -> "Q3 - Summer";
        case 3 -> "Q3 - Summer";
        default -> "Unknown quarter";
    }

如果 case 块中只有一条语句,则该语句生成的值将由 switch 表达式返回。
在代码块的情况下,语法略有不同。传统上,return 关键字用于表示代码块生成的值。遗憾的是,这种语法在 switch 语句中会导致歧义。让我们考虑以下示例。这段代码无法编译,仅作为示例存在。

// Be careful, this code does not compile!
public String convertToLabel(int quarter) {
    String quarterLabel =
        switch( quarter){
            case 0 -> {
                IO.println("Q1 - Winter");
                return "Q1 - Winter";
            }
            default -> "Unknown quarter";
        };
        return quarterLabel;
}

quarter 等于 0 时执行的代码块需要返回一个值。它使用 return 关键字来表示该值。仔细观察这段代码,你会发现存在两个 return 语句:一个位于 case 代码块中,另一个位于方法主体中。这正是歧义所在:人们可能会疑惑第一个 return 的语义含义——它表示程序将带着此值退出方法?还是说将退出 switch 语句?此类歧义导致代码可读性差且易出错。
为了解决这种歧义,创建了一种新的语法:yield语句。前面的示例代码应改写为以下形式。

public String convertToLabel(int quarter) {
    String quarterLabel =
        switch( quarter){
            case 0 -> {
                IO.println("Q1 - Winter");
                yield "Q1 - Winter";
            }
            default -> "Unknown quarter";
        };
        return quarterLabel;
}

yield 语句是一种可在 switch 语句任意 case 块中使用的语句。它会返回一个值,该值将成为包含它的 switch 语句的返回值。

增加默认分支

这段我没看太明白。switch expression 和 switch statement 一样,建议总是包含 default 分支,以防输入值不在预期范围内。

在 switch 表达式中使用冒号规则

讲真,这段我也没看太明白。我个人不推荐在 switch expressions 中 使用 switch statements 的冒号规则,容易混淆

Null 值处理

Java SE 17 引入了对 switch 表达式中 null 值的处理支持。您可以在 switch 表达式中添加一个 case null 分支,以处理可能为 null 的输入值。例如:

public class SwitchExpressionDemo {

    public static void main(String[] args) {
        IO.println(processInput(null));
    }

    static String processInput(String input) {
        return switch (input) {
            // 显式处理 null
            case null ->
                "输入为空,请提供有效值。";

            // 处理其他有效字符串
            case "A" ->
                "处理类型 A";
            case "B" ->
                "处理类型 B";

            // default 块处理所有非 null 且未匹配的值
            default ->
                "未知输入";
        };
    }
}

程序运行结果为:

输入为空,请提供有效值。