Bon*_*ond 1 java switch-statement java-21
给出sealed如下层次结构
sealed interface Shape permits Rectangle, Square
record Rectangle() implements Shape
record Square() implements Shape
Run Code Online (Sandbox Code Playgroud)
由于Rectangle&Square是记录,它本质上使整个层次结构不可扩展,即不再允许有更多的子类。
从 JDK 21 开始,模式匹配switch强制 switch 通过覆盖所有可能的内容case或提供一个default案例来覆盖其余内容,从而实现详尽无遗。
因此,在下面switch什么情况下default会执行案例,因为涵盖了所有可能的组合,为什么甚至允许这样做?
switch (shape) {
case Rectangle r -> // do something with r;
case Square sq -> // do something with sq;
case null -> // shape could be null
default -> // WHY is this permitted when all possible cases are covered already??
}
Run Code Online (Sandbox Code Playgroud)
PS:密封的层次结构肯定可以进化,但是当这种情况发生时,编译器也会自动标记switch为升级自身。
到目前为止,答案大多是正确的,但故事还有更多内容。
到目前为止给出的简单答案是正确的,即无论编译时类型检查是否详尽,都可能存在不匹配任何大小写的运行时值。允许使用默认类,因为它有可能被选择(如果您不提供默认类,编译器会为您提供一个抛出 MatchException 的合成类。)
编译时详尽的开关在运行时可能并非真正详尽的原因有两个:单独编译和剩余。
其他答案中已充分处理了单独编译;新颖的枚举常量和密封类型的新颖子类型可以在运行时出现,因为可以重新编译层次结构而无需重新编译其上的开关。这通常是由编译器默默地为您提供的(没有必要让您声明一个default仅抛出“无法到达此处”的子句),但如果您愿意,您可以自己处理它。
第二个原因是剩余,它反映了“足够穷举”的合理含义和实际的穷举并不完全一致,如果我们要求开关真正穷举,那么编程起来就很无趣了。
一个简单的例子是这样的:
Box<Box<String>> bbs = ...
switch (bbs) {
case Box(Box(String s)): ...
}
Run Code Online (Sandbox Code Playgroud)
这个开关应该彻底打开吗Box<Box<String>>?事实证明,在运行时有一个可能的值不匹配:Box(null)。(回想一下,嵌套模式与外部模式匹配,然后使用外部模式的绑定作为内部模式的匹配候选——并且记录模式不能匹配 null,因为它想要调用组件访问器。)
我们可以要求详尽无遗,为 提供一个单独的错误处理案例Box(null),但没有人会喜欢这样,并且如果没有那么简单的示例,错误处理将压倒有用的案例。因此,Java 做出了务实的选择,用“人类”术语定义了 switch 的详尽性(对于合理的类来说似乎是详尽的代码),并允许通过合成默认值来处理“愚蠢”的情况。(如果您愿意,您仍然可以自由地显式处理任何愚蠢的情况。)此开关被认为是详尽的,但具有非空余数。
整个概念在模式:穷举性、无条件性和余数中进行了更详细的解释。