JEP 361 Switch 表达式
一、概要
扩展 switch,使其可以作为语句或表达式使用,以及使两种形式都可以使用传统的 case ... : 标签(具有穿透效果)或新的 case ... -> 标签(没有穿透效果),并添加一个新的语句用于从 switch 表达式中产生值。这些更改将简化日常编码,并为在 switch 中使用模式匹配铺平道路。这是 JDK 12 和 JDK 13 中的一个预览语言特性。
扩展 switch,使其既可以用作语句,也可以用作语句,并且这两种形式都可以使用传统的 case ...: 标签,或新的 case ...-> 标签。并添加了一个新的语句用于从 switch 表达式中生成值。这些更改将简化日常编码,并为在 switch 中使用模式匹配做好准备。这是 JDK 12 和 JDK 13 中的一个预览语言特性。
二、历史
JEP 325 在 2017 年 12 月提出了 switch 表达式。JEP 325 在 2018 年 8 月作为 JDK 12 的预览功能。其中的一个特性是重载 break 语句以从 switch 表达式获取返回值。但是在 JDK 12 的反馈表明这种 break 的使用是令人困惑的。所以,作为对 JEP 325 反馈的回应,JEP 354 作为 JEP 325 的演变被创建。
JEP 354 提出了一种新的语句 yield,并且恢复了 break 原本的含义。JEP 354 在 2019 年 6 月作为 JDK 13 的预览功能。对 JDK 13 的反馈表明,switch 已经不用再进行任何更改,可以作为 JDK 14 中的最终和永久版本的表达式。
三、动机
当 JDK 的开发人员准备增强 Java 编程语言以支持模式匹配(JEP 305: Pattern Matching for instanceof (Preview) (openjdk.org))时,现有的 switch 语句的一些长期困扰用户的的不规则性成为了阻碍。其中包括开关标签之间的默认控制流行为(case 语句贯穿)、default 块,以及 switch 仅仅作为一个语句的事实。
虽然这种传统的控制流通常适用于便捷低级代码,但是由于 switch 在高层级上下文中的使用,其容易出错的特性开始超过其灵活性。
例如,在下面的代码中,许多语句使其变得冗长,而这种视觉上的繁杂经常会掩盖难以调试的错误,其中丢失的语句意味着错误的发生。
1 | switch (day) { |
引入了一种新形式的标签,如果标签匹配,则仅执行标签右侧的代码。上面的代码可以改成这样:
1 | switch (day) { |
-> 标签右侧的代码被限制为表达式、块或语句。这有一个令人愉悦的效果:如果一个分支的代码块中声明了一个局部变量,它只能在当前分支块中被使用,不能在其他分支中使用。这消除了传统 switch 的另一个烦恼,即局部变量的范围是整个开关块。示例如下:
1 | switch (day) { |
1 | switch (day) { |
许多现有的语句,其中每个分支要么分配给一个共同的目标变量,要么返回一个值,就像下面这样:
1 | int numLetters; |
上面的代码可以这样代替:
1 | int numLetters = switch (day) { |
四、详情
1、箭头标签
1 | switch (day) { |
2、Switch 表达式
1 | int numLetters = switch (day) { |
3、yield 关键字
大多数开关表达式都会在 -> 右侧有一个表达式。因为有时候需要一个完整的块,所以引入了一个新的 yield 语句来产生一个值。
1 | int j = switch (day) { |
像 switch 语句一样,switch 表达式也可以使用带有 case 标签的传统 switch 块。
1 | int result = switch (string) { |
break 和 yield 这两个语句有助于轻松消除 switch 语句和 switch 表达式之间的歧义。
yield 不是一个关键字,而是一个受限制的标识符(比如 var),这意味着命名为 yield 的类是不合法的。
4、扩展
在 switch 表达式(2、Switch 表达式)中,所涉及到的分支场景必须要是全面的,即所有的情况必须要涉及一个 case 标签。然而 switch 语句不需要覆盖所有的情况。
这意味着,一个 default 语句块是必须的。在一个覆盖所有情况的枚举 switch 表达式中,编译器会插入一个默认子句,用来指示枚举定义在编译时和运行时之间发生了更改。隐含地插入默认子句,使得代码更加健壮。当编译器编译代码时,编译期会检查是否显示处理了所有情况,如果开发人员插入了一个显示的默认子句,这可能会隐含一个错误。
此外,控制语句 break, yield, return, continue 不能跳过开关表达式,如下:
1 | for (int i = 0; i < 10; i++) { |
相关链接
[[JEP 354 Switch 表达式]]
[[JEP 325 Switch 表达式]]
JEP 361: Switch Expressions (openjdk.org)
JEP 305: Pattern Matching for instanceof (Preview) (openjdk.org)
OB links
OB tags
#JDK新特性