使用 Switch 表达式进行分支
修改 Switch 语法
在 Java SE 14 中,您可以使用另一种更方便的 switch
关键字语法:switch
表达式。
有几个因素促使了这种新语法的出现。
- switch 标签之间的默认控制流行为是贯穿执行。这种语法容易出错,会导致应用程序出现错误。
switch
块被视为一个块。如果您需要仅在一个特定的case
中定义一个变量,这可能是一个障碍。switch
语句是一个语句。在前面几节的示例中,每个case
中都会给一个变量赋值。将其变成表达式可以使代码更简洁、更易读。
前面一节中介绍的语法,称为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;
}
System.out.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;
};
System.out.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 -> {
System.out.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 -> {
System.out.println("Q1 - Winter");
yield "Q1 - Winter";
}
default -> "Unknown quarter";
};
}
return quarterLabel;
}
yield
语句是一个可以在 switch
语句的任何 case
块中使用的语句。它带有一个值,该值将成为封闭 switch
语句的值。
添加默认子句
默认子句允许您的代码处理选择器值与任何 case
常量都不匹配的情况。
switch 表达式的 case 必须是穷举的。对于所有可能的值,都必须有一个匹配的 switch 标签。switch 语句不需要是穷举的。如果选择器目标与任何 switch 标签都不匹配,则此 switch 语句将不会执行任何操作,而是静默地执行。这可能是应用程序中隐藏错误的地方,您应该避免这种情况。
在大多数情况下,可以使用 default
子句来实现穷举性;但是,在覆盖所有已知常量的 enum
switch
表达式的情况下,您不需要添加此 default
子句。
仍然需要处理一种情况。如果有人在枚举中添加了一个枚举值,但忘记更新此枚举上的 switch 语句,会发生什么?为了处理这种情况,编译器会在穷举的 switch 语句中为您添加一个 default
子句。此 default
子句在正常情况下永远不会执行。只有在添加了枚举值并且会抛出 IncompatibleClassChangeError
时才会执行。
处理穷举性是 switch
表达式的一项功能,传统 switch
语句没有提供此功能,并且在 switch
对枚举值以外的其他情况中使用。
在 Switch 表达式中编写冒号 Case
switch
表达式也可以使用带有 case L:
的传统 case
块。在这种情况下,贯穿执行语义适用。使用 yield
语句生成值。
int quarter = ...; // any value
String quarterLabel =
switch (quarter) {
case 0 : yield "Q1 - Winter";
case 1 : yield "Q2 - Spring";
case 2 : yield "Q3 - Summer";
case 3 : yield "Q3 - Summer";
default: System.out.println("Unknown quarter");
yield "Unknown quarter";
};
处理空值
到目前为止,switch
语句不接受空选择器值。如果您尝试对空值进行 switch
,您将收到 NullPointerException
。
Java SE 17 具有一个预览功能,该功能增强了 switch
表达式以允许空值,因此您可以预期这种情况会发生变化。
上次更新: 2021 年 9 月 22 日