JEP 441:switch的模式匹配
原文:https://openjdk.org/jeps/441
翻译:张欢
使用switch
表达式和语句的模式匹配增强Java编程语言。将模式匹配扩展到switch
中,允许根据多种模式测试表达式,每种模式都有特定的操作,这样就可以简洁而安全地表达复杂的面向数据的查询。
历史
此特性最初由JEP 406(JDK 17)提出,随后由JEP 420(JDK 18)、427(JDK 19)和433(JDK 20)改进。它与记录模式特性(JEP 440)共同发展,两者有相当大的互动。本JEP提议根据持续的的经验和反馈,通过进一步的小幅改进来完成此特性。
除了各种编辑性的更改外,基于上一个JEP的主要变化是:
- 删除带括号的模式,因为它们没有足够的价值,并且
- 允许在
switch
表达式和语句中,使用合格的枚举常量作为case
常量。
目标
- 通过允许在
case
标签中出现模式,扩展switch
表达式和语句的表现力和适用性。 - 允许在需要时放宽
switch
的历史性null
值限制。 - 通过要求模式
switch
语句涵盖所有可能的输入值来提高switch
语句的安全性。 - 确保所有现有的
switch
表达式和语句继续编译,而不发生任何变化,并以相同的语义执行。
动机
在Java 16中,JEP 394扩展了instanceof
运算符,使其能够采用类型模式并执行模式匹配。这种适度的扩展简化了熟悉的“instanceof-and-cast”用法,使其更加简洁且不易出错:
在新代码中,如果在运行时obj
的值是String
的实例,那么obj
与类型模式String s
匹配。如果模式匹配,则instanceof
表达式为true
,模式变量s
被初始化为obj
的值并转换为String
,然后可以在包含的块中使用该值。
我们经常想将一个变量(例如obj
)与多个替代方案进行比较。Java支持使用switch
语句进行多向比较,并且自Java 14以来,还支持switch
表达式 (JEP 361),但不幸的是switch
非常受限。我们只能对几种类型的值进行switch
操作——整数基本类型(不包括long
)、它们对应的包装类、枚举类型和String
——并且我们只能测试与常量的完全相等性。我们可能希望使用模式针对多种可能性测试同一个变量,并对每种可能性采取特定的操作,但由于现有的switch
不支持这一点,我们最终会得到一系列if...else
测试,例如:
此代码得益于使用模式instanceof
表达式,但它远非完美。首先,这种方法允许隐藏编码错误,因为我们使用了过于通用的控制构造。目的是在if...else
链的每个分支中将某些内容分配给formatted
,但没有什么可以让编译器识别并强制执行该不变量。如果某个“then”块(可能是很少执行的块)没有分配给formatted
,则我们有一个错误。(将formatted
声明为空白的局部变量至少会在此工作中引入编译器的明确赋值分析,但开发人员并不总是编写此类声明。)此外,上述代码不可优化;如果没有编译器优化,它将具有 的时间复杂度,即使底层问题通常是 。
但是switch
非常适合模式匹配!如果我们扩展switch
语句和表达式以适用于任何类型,并允许case
标签带有模式而不仅仅是常量,那么我们可以更清晰、更可靠地重写上述代码:
这个switch
的语义很明确:如果选择器表达式obj
的值与模式匹配,则应该用带有模式的case
标签。(为了简洁起见,我们展示了switch
表达式,但也可以展示switch
语句;switch
块(包括case
标签)将保持不变。)
由于我们使用了正确的控制结构,此代码的意图更加清晰:我们说,“参数obj
最多匹配以下条件之一,找出并执行相应的分支。”另外,它更易于优化;在这种情况下,我们更有可能在 时间内执行调度。
switch与null
传统上,如果选择器表达式的计算结果为null
,则switch
语句和表达式将抛出NullPointerException
,因此必须在switch
之外进行null
测试:
当switch
仅支持少数引用类型时,这是合理的。但是,如果switch
允许任何引用类型的选择器表达式,并且case
标签可以具有类型模式,那么独立的null
测试感觉就像是一种刻意的区分,它会带来不必要的样板和出错机会。最好通过允许新的case null
标签将null
测试集成到switch
中:
当选择器表达式的值为null
时,switch
的行为始终由其case
标签决定。当存在case null
时,switch
将执行与该标签关联的代码;当不存在case null
时,switch
将抛出NullPointerException
,与之前一样。(为了保持与switch
当前语义的向后兼容性,default
标签不匹配null
选择器。)
case的细化
与带有常量的case
标签相比,模式case
标签可以应用于许多值。这通常会导致switch
规则右侧出现条件代码。例如,考虑以下代码:
这里的问题是,使用单一模式来区分多个case
,无法扩展到单一条件之外。我们更愿意编写多个模式,但我们需要某种方式来表达对模式的细化。因此,我们允许switch
块中的when
子句指定模式case
标签的守护,例如case String s when s.equalsIgnoreCase("YES")
。我们将这样的case
标签称为被守护的case
标签,将布尔表达式称为守护。
通过这种方法,我们可以使用守护重写上述代码:
这带来了一种更易读的switch
编程风格,其中测试的复杂性出现在switch
规则的左侧,而当该测试得到满足时,适用的逻辑出现在switch
规则的右侧。
我们可以使用针对其他已知常量字符串的额外规则进一步增强此示例:
这些示例展示了如何结合使用case
常量、case
模式和null
标签来展示switch
编程的新特性:我们可以将以前与业务逻辑混合的复杂条件逻辑简化为可读的、顺序的switch
标签列表,其中业务逻辑位于switch
规则的右侧。
switch与枚举常量
目前,case
标签中枚举常量的使用受到严格限制:switch
的选择器表达式必须是枚举类型,标签必须是枚举常量的简单名称。例如:
即使添加了模式标签,此约束也会导致不必要的冗长代码。例如:
如果我们可以为每个枚举常量设置一个单独的case
,而不是使用大量的守护模式,那么此代码的可读性会更高。因此,我们放宽了选择器表达式必须是枚举类型的要求,并允许case
常量使用枚举常量的限定名称。这允许将上述代码重写为:
现在,我们每个枚举常量都有一个直接的实例,而无需使用守护的类型模式,这些模式以前只是为了解决类型系统的当前约束。
描述
我们通过四种方式增强了switch
语句和表达式:
- 改进枚举常量
case
标签, - 扩展
case
标签以包含常量之外的模式和null
, - 扩大
switch
语句和switch
表达式的选择器表达式允许的类型范围(以及对switch
块详尽性进行必要的、更丰富的分析),以及 - 允许可选的
when
子句跟在case
标签之后。
改进的枚举常量与case标签
长期以来的一项要求是,在switch
处理枚举类型时,唯一有效的case
常量是枚举常量。但这是严格的要求,随着新的、更丰富的switch
形式的出现,它变得繁重起来。
为了保持与现有Java代码的兼容性,在switch
处理枚举类型时,case
常量仍然可以使用被处理的枚举类型常量的简单名称。
对于新代码,我们扩展了枚举的处理方式。首先,我们允许枚举常量的限定名称作为case
常量出现。这些限定名称可在处理枚举类型时使用。
其次,当枚举常量之一的名称用作case
常量时,我们不再要求选择器表达式为枚举类型。在这种情况下,我们要求名称符合条件,并且其值与选择器表达式的类型兼容。(这使枚举case
常量与数值case
常量的处理保持一致。)
例如,允许以下两个方法:
以下示例是不允许的:
switch标签中的模式
我们修改了switch
块中的switch
标签的语法如下(对比JLS §14.11.1):
主要的增强特性是引入了一个新的case p
标签,其中p
是一个模式。switch
的本质保持不变:将选择器表达式的值与switch
标签进行比较,选择其中一个标签,然后执行或运算与该标签关联的代码。现在的区别在于,对于带有模式的case
标签,所选标签由模式匹配的结果而不是相等性测试决定。例如,在下面的代码中,obj
的值与模式Long l
匹配,并运算与标签case Long l
关联的表达式:
在模式匹配成功后,我们经常会进一步测试匹配的结果。这可能会导致繁琐的代码,例如:
所需的测试——obj
是长度为1的String
——不幸地被拆分在模式case
标签和它下面的if
语句之间。
为了解决这个问题,我们引入了守护模式case
标签,允许可选的守护(布尔表达式)跟在模式标签后面。这允许重写上述代码,以便将所有条件逻辑提升到switch
标签中:
如果obj
是String
并且长度为1,则第一个子句匹配。如果obj
是任意长度的String
,则第二个子句匹配。
只有模式标签才可以有守护。例如,编写同时带有case
常量和守护的标签是无效的;如:case "Hello" when callRandomBooleanExpression()
。
在switch
中支持模式时,需要考虑五个主要的语言设计领域:
- 增强类型检查
switch
表达式和语句的详尽性- 模式变量声明的作用域
- 处理
null
- 错误
增强的类型检查
选择器表达式的类型
在switch
中支持模式,意味着我们可以放宽对选择器表达式类型的限制。目前,普通switch
的选择器表达式的类型必须是整型基本类型(不包括long
)、相应的包装类(即Character
、Byte
、Short
或Integer
)、String
或枚举类型。我们对此进行了扩展,并要求选择器表达式的类型是整型基本类型(不包括long
)或任何引用类型。
例如,在下面的switch
模式中,选择器表达式obj
与涉及class
、enum
、record
和数组的类型模式以及case null
标签和default
进行匹配:
switch
块中的每个case
标签都必须与选择器表达式兼容。对于带有模式的case
标签(称为模式标签),我们使用现有的表达式与模式兼容性概念(JLS §14.30.1)。
case标签的涵盖性
支持模式case
标签意味着,对于选择器表达式的给定值,现在可以应用多个case
标签,而以前最多只能应用一个case
标签。例如,如果选择器表达式求值为String
,则case
标签case String s
和case CharSequence cs
都将适用。
要解决的第一个问题是,确定在这种情况下应该应用哪个标签。我们不会尝试复杂的最佳匹配方法,而是采用更简单的语义:选择switch
块中出现的第一个适用于值的case
标签。
在这个例子中,如果obj
的值是String
类型,那么将应用第一个case
标签;如果它是CharSequence
类型而不是String
类型,那么将应用第二个模式标签。
但是如果我们交换这两个标签的顺序会发生什么?
现在,如果obj
的值是String
类型,则case CharSequence
标签适用,因为它首先出现在switch
块中。case String
标签是无法访问的,因为没有选择器表达式的值会导致它被选中。与无法访问的代码类似,这被视为编程错误并导致编译时报错。
更确切地,我们说第一个标签case CharSequence cs
涵盖了第二个标签case String s
,因为与模式String s
匹配的每个值也与模式CharSequence cs
匹配,但反之则不然。这是因为第二个模式的类型String
是第一个模式的类型CharSequence
的子类型。
在相同的模式中,非守护模式的标签涵盖守护模式的标签。例如,(非守护)模式的标签case String s
涵盖了守护模式的标签case String s when s.length > 0
,因为与标签case String s when s.length > 0
匹配的每个值都一定与标签case String s
匹配。
对于一个守护模式标签和另一个(守护或非守护)模式标签,当且仅当前者的模式涵盖后者的模式,并且前者的守护是一个取值为true
的常量表达式时,前者才会涵盖后者。例如,守护模式标签case String s when true
涵盖了模式标签case String s
。我们不再进一步分析守护表达式,以便更准确地确定哪些值与模式标签匹配——这个问题通常是无法判定的。
模式标签可以涵盖常量标签。例如,当A
是枚举类型E
的成员时,模式标签case Integer i
涵盖了常量标签case 42
,而当A
是枚举类型E
的成员时,模式标签case E e
涵盖了常量标签case A
。如果非守护的相同模式标签涵盖常量标签,那么守护的模式标签也将涵盖常量标签。换句话说,我们不检查守护与否,因为这通常是不可判定的。例如,模式标签case String s when s.length() > 1
涵盖了常量标签case "hello"
,正如预期的那样;但是case Integer i when i != 0
涵盖了标签case 0
。
所有这些都表明了标签的简单、可预测和可读的顺序,其中常量标签应该出现在守护模式标签之前,而守护模式标签应该出现在非守护模式标签之前:
编译器会检查所有case
标签。如果switch
块中的case
标签被该switch
块中的任何前一个case
标签所涵盖,则会出现编译时错误。此涵盖要求可确保,如果switch
块仅包含类型模式case
标签,则它们将按子类型顺序出现。
(涵盖的概念类似于try
语句的catch
子句上的条件,如果捕获异常类E
的catch
子句前面有一个可以捕获E
或E
的超类的catch
子句,则会出现错误(JLS §11.2.3)。从逻辑上讲,前面的catch
子句优先于后面的catch
子句。)
如果switch
表达式或switch
语句的switch
块具有多个可以“匹配所有”的switch
标签,也会导致编译时错误。“匹配所有”的标签是default
和模式case
标签,其中模式无条件匹配选择器表达式。例如,类型模式String s
无条件匹配String
类型的选择器表达式,类型模式Object o
无条件匹配任何引用类型的选择器表达式:
switch表达式和语句的详尽性
类型覆盖
switch
表达式要求在switch
块中处理选择器表达式的所有可能值;换句话说,它必须是详尽的。这保持了switch
表达式只要成功,就始终会产生结果值的属性。
对于普通的switch
表达式,此属性由switch
块上的一组简单的额外条件强制执行。
对于模式switch
表达式和语句,我们通过定义switch
块中switch
标签的类型覆盖概念来实现这一点。然后,将switch
块中所有switch
标签的类型覆盖组合起来,以确定switch
块是否穷尽了选择器表达式的所有可能性。
考虑这个(错误的)模式切换表达式:
switch
块只有一个switch
标签,case String s
。它匹配类型是String
子类型的obj
的任何值。因此,我们说这个switch
标签的类型覆盖是String
的每个子类型。这个模式switch
表达式并不详尽,因为其switch
块的类型覆盖(String
的所有子类型)不包括选择器表达式的类型(Object
)。
考虑这个(仍然是错误的)例子:
此switch
块的类型覆盖是其两个switch
标签的覆盖率的并集。换句话说,类型覆盖是String
的所有子类型的集合,以及Integer
的所有子类型的集合。但是,类型覆盖仍然不包括选择器表达式的类型,因此该模式switch
表达式也不详尽,并会导致编译时错误。
default
标签的类型覆盖范围是每种类型,因此这个例子(终于!)是合法的:
实践中的详尽性
类型覆盖的概念已存在于非模式switch
表达式中。例如:
此枚举类上的switch
表达式并不详尽,因为预期的输入YELLOW
未被覆盖。正如预期的那样,添加一个case
标签来处理YELLOW
枚举常量足以使switch
详尽无遗:
以这种方式编写的switch
非常详尽,具有两个重要的好处。
首先,编写一个default
子句会很麻烦,因为它可能会引发异常,由于我们已经处理了所有的情况:
在这种情况下手动编写default
子句不仅令人恼火,而且实际上有害,因为编译器在没有default
子句的情况下可以更好地检查详尽性。(对于任何其他“匹配所有”子句,如default
、case null,default
或无条件类型模式,情况也是如此。)如果我们省略default
子句,那么我们将在编译时发现我们是否忘记了case
标签,而不是在运行时发现——甚至可能在运行时也发现不了。
更重要的是,如果有人后续向Color
枚举添加了另一个常量,会发生什么情况?如果我们有一个显式的“匹配所有”子句,那么我们只会在运行时发现新的常量值。但是,如果我们对switch
进行编码以覆盖编译时已知的所有常量,并省略“匹配所有”子句,那么我们将在下次重新编译包含switch
的类时发现此更改。“匹配所有”子句可能会掩盖详尽性错误。
结论是:如果可能的话,没有“匹配所有”子句的详尽switch
比带有“匹配所有”子句的详尽switch
更好。
从运行时来看,如果添加了新的Color
常量,而包含switch
的类未重新编译,会发生什么情况?存在一种风险,即新常量暴露给我们的switch
。由于这种风险始终存在于枚举中,因此如果详尽的枚举switch
没有匹配所有子句,则编译器将合成一个引发异常的default
子句。这保证了switch
无法在不选择其中一个子句的情况下正常完成。
详尽性的概念旨在,在“覆盖所有合理情况”与“不强迫你编写可能污染甚至主宰你的代码,但实际价值不大的罕见极端情况”之间取得平衡。换句话说:详尽性是真正的运行时详尽性的编译时近似值。
详尽性与密封类
如果选择器表达式的类型是密封类 (JEP 409),则类型覆盖检查可以根据密封类的permits
子句来确定switch
块是否详尽。这有时可以消除对default
子句的需求,如上所述,这是一种很好的做法。考虑以下密封接口S
的示例,该接口具有三个允许的子类A
、B
和C
:
编译器可以确定switch
块的类型覆盖范围是类型A
、B
和C
。由于选择器表达式S
的类型是密封接口,其允许的子类恰好是A
、B
和C
,因此该switch
块是详尽的。所以,不需要default
标签。
当允许的直接子类仅实现(泛型)密封超类的特定参数化时,需要格外小心。例如:
I
仅有的允许子类是A
和B
,但是编译器可以检测到switch
块只需要覆盖类B
就可以详尽,因为选择器表达式的类型是I<Integer>
,并且A
的参数化都不可能是I<Integer>
的子类型。(译者注:A
的参数化只能是I<String>
的子类型。)
再次强调,详尽性的概念只是一种近似。由于是分开编译的,接口I
的新实现可能会在运行时出现,因此编译器将在这种情况下插入一个会抛出异常的合成default
子句。
由于记录模式可以嵌套,因此记录模式(JEP 440)使详尽性的概念变得更加复杂。因此,详尽性的概念必须反映这种潜在的递归结构。
详尽性和兼容性
详尽性要求适用于模式switch
表达式和模式switch
语句。为确保向后兼容性,所有现有switch
语句都将按原样编译。但如果switch
语句使用本JEP中描述的任何switch
增强特性,则编译器将检查它是否详尽。(Java语言的未来编译器可能会对不详尽的旧版switch
语句发出警告。)
更确切地说,任何使用模式或null
标签或其选择器表达式不是传统类型(char
、byte
、short
、int
、Character
、Byte
、Short
、Integer
、String
或枚举类型)的switch
语句都需要做到详尽。例如:
模式变量声明的作用域
模式变量(JEP 394)是由模式声明的局部变量。模式变量声明的不同寻常之处在于其作用域是流程敏感的。回顾以下示例,其中类型模式String s
声明了模式变量s
:
s
的声明在代码中模式变量s
将被初始化的部分作用域之内。在此示例中,即在&&
表达式的右侧操作和“then”块中。但是,s
不在“else”块的作用域内:为了将控制权转移到“else”块,模式匹配必须失败,在这种情况下模式变量将不会被初始化。
我们扩展了模式变量声明的这种流程敏感作用域概念,以包含出现在case
标签中的模式声明,并制定了三条新规则:
- 出现在守护的
case
标签模式中的模式变量,声明的作用域包括其守护,即when
表达式。 - 出现在
switch
规则的case
标签中的模式变量,声明的作用域包括箭头右侧出现的表达式、块或throw
语句。 - 出现在
switch
标签语句组的case
标签中的模式变量,声明的作用域包括语句组的块语句。禁止通过声明模式变量的 case 标签。
该示例显示了第1条规则的实际应用:
模式变量c
的声明作用域包括其守护,即表达式c.charValue() == 7
。
这个变体展示了第2条规则的实际作用:
这里模式变量c
的声明作用域是第一个箭头右边的块。模式变量i
的声明作用域是第二个箭头右边的throw
语句。
第3条规则更加复杂。我们首先考虑一个例子,其中switch
标签语句组只有一个case
标签:
模式变量c
的声明作用域包括语句组的所有语句,即两个if
语句和一个println
语句。该作用域不包括default
语句组的语句,尽管第一个语句组的执行可以“贯穿”(fall-through)到default
标签并执行这些语句。
我们禁止通过声明模式变量的case
标签来“贯穿”的可能性。考虑这个错误示例:
如果允许这样做,并且obj
的值是Character
,则switch
块的执行可能会贯穿到case Integer i:
之后的第二个语句组,其中模式变量i
尚未初始化。因此,允许执行贯穿声明模式变量的case
标签是编译时错误。
这就是为什么不允许使用由多个模式标签组成的switch
标签,例如case Character c: case Integer i: ...
。类似的原因也适用于禁止在单个case
标签中使用多个模式:不允许使用case Character c, Integer i: ...
或case Character c, Integer i -> ...
。如果允许这样的case
标签,那么c
和i
都将在冒号或箭头之后的范围中,但只有其中一个会被初始化,具体取决于obj
的值是Character
还是Integer
。
另一方面,贯穿未声明模式变量的标签是安全的,如下例所示:
处理null
传统上,如果选择器表达式的计算结果为null
,则switch
会抛出NullPointerException
。这是众所周知的行为,我们不建议对任何现有的switch
代码进行更改。但是,对于模式匹配和null
值,存在合理且不引发异常的语义,因此在模式switch
块中,我们能够以更常规的方式处理null
,同时保持与现有switch
语义的兼容性。
首先,我们引入了一个新的case null
标签。然后,我们取消了总规则,即如果选择器表达式的值为null
,则switch
立即抛出NullPointerException
。相反,我们检查case
标签以确定switch
的行为:
- 如果选择器表达式的计算结果为
null
,则任何case null
标签都被视为匹配。如果switch
块没有关联的标签,则switch
会像以前一样抛出NullPointerException
。 - 如果选择器表达式的计算结果为非
null
值,则我们像往常一样选择匹配的case
标签。如果没有case
标签匹配,则任何default
标签都被视为匹配。
例如,给定下面的声明,执行nullMatch(null)
将打印null!
而不是抛出NullPointerException
:
没有case null
标签的switch
块将被视为具有case null
规则,该规则的主体会抛出NullPointerException
。换句话说,此代码:
等效于:
在这两个例子中,执行nullMatch(null)
将导致抛出NullPointerException
。
我们保留了现有switch
构造中的直觉,即执行对null
的处理是一件非常特殊的事情。模式switch
的不同之处在于,你可以直接在switch
内部处理这种情况。如果你在switch
块中看到case null
标签,则该标签将与null
值匹配。如果你在switch
块中没有看到case null
标签,则处理null
值将像以前一样抛出NullPointerException
。因此,switch
块中对null
值的处理得到了规范化。
将case null
与default
相结合是有意义的,而且并不罕见。为此,我们允许case null
标签具有可选的default
;例如:
如果obj
的值是null
引用值,或者其他case
标签均不匹配,则obj
的值与此标签匹配。
如果switch
块同时具有case null, default
标签和另一个default
标签,那么会导致编译时错误。
错误
模式匹配可能会中断完成。例如,当将值与记录模式进行匹配时,记录的访问器方法可能会中断完成。在这种情况下,模式匹配被定义为通过抛出MatchException
来中断完成。如果此类模式作为标签出现在switch
中,则switch
也会通过抛出MatchException
而中断完成。
如果case
模式有一个守护,并且对守护的评估中断完成,则switch
也会因相同的原因中断完成。
如果模式switch
中没有标签与选择器表达式的值匹配,则switch
会通过抛出MatchException
中断完成,因为模式switch
必须是详尽的。
例如:
调用exampleAnR(new R(42))
导致抛出MatchException
。(总是抛出异常的记录访问器方法是极不正常的,而抛出MatchException
的、详尽的模式switch
也是极不寻常的。)
相比之下:
调用example(new R(42))
导致抛出ArithmeticException
。
为了与模式switch
语义保持一致,当运行时没有应用任何switch
标签时,枚举类上的switch
表达式现在会抛出MatchException
而不是IncompatibleClassChangeError
。这是对语言的一个小的不兼容更改。(只有在编译switch
后更改枚举类时,枚举上的详尽switch
才会匹配失败,这种情况非常罕见。)
未来的工作
- 目前,模式切换不支持基本类型
boolean
、long
、float
和double
。允许这些基本类型也意味着允许它们出现在instanceof
表达式中,并将基本类型模式与引用类型模式对齐,这将需要大量额外工作。这留给未来可能的JEP来完成。 - 我们期望,将来,泛型类将能够声明解构模式来指定如何匹配它们。此类解构模式可以与模式
switch
一起使用,以产生非常简洁的代码。例如,如果我们有一个Expr
层次结构,其子类型为IntExpr
(包含一个int
)、AddExpr
和MulExpr
(包含两个Expr
)以及NegExpr
(包含一个Expr
),我们可以匹配Expr
并对特定子类型采取行动,只需一步: 如果没有这样的模式匹配,表达这样的临时多态计算需要使用繁琐的访问者模式。模式匹配通常更透明、更直接。 - 添加AND和OR模式也可能很有用,以便为带有模式的
case
标签提供更多的表现力。
备选方案
- 我们可以定义一个类型
switch
,仅支持选择器表达式类型的switch
,而不是支持模式switch
。此特性更易于指定和实现,但表达能力却差得多。 - 守护模式标签有许多其他语法选项,例如
p where e
、p if e
,甚至p &&& e
。 - 守护模式标签的替代方法是直接将守护模式作为特殊模式形式来支持,例如
p && e
。在早期预览版中尝试过这种方法后,布尔表达式产生的歧义导致我们更喜欢守护case
标签而不是守护模式。
依赖
本JEP以JDK 16中提供的*instanceof
模式匹配*(JEP 394) 以及Switch表达式(JEP 361)提供的增强特性为基础。它与记录模式(JEP 440)共同发展。