Java 的最新版本(12 及更高版本)已经在 switch 上投入了大量工作。现在有很多形式。其中一些形式的基本原则强烈表明某些新的句法特征很重要,为了保持一致性,如果该特征也可以用于其他形式,那么其他形式也会更新。
历史
最初的 java 1.0 开关是 C 的直接副本(这解释了它们完全奇怪和愚蠢的语法;这就是 C 所拥有的,而 java 旨在让 C 编码人员感到舒适。我们可以抱怨这样一个事实,即这意味着语法是愚蠢的,但很难否认 C 风格的语法,不管它看起来多么疯狂,已经占领了这个星球,C、C#、Java 和 Javascript 加在一起占据了“市场”的很大一部分,所以说话)。
它:
- 要求您切换的东西在
int 上,并且只有int
- 每个案例都是一个特定且恒定的数字(没有
case 10-100,绝对没有case <5 或case x:)。
- 它是一个语句(switch 本身没有值,它是一种有条件地跳转到语句的方式)。
- 与 C 版本不同,将开关的性质视为奇怪的 GOTO 式构造,因此实际上不允许您通过它交错控制结构,例如
do/while 循环(幸运的是,我猜)? - 所以,Duff's Device 在 java 中是不合法的。
最近更新:
与枚举一起:开关中的枚举
Java1.5 带来了枚举,并且由于枚举旨在替换 int 常量,它们需要在开关中可用。鉴于枚举值有一个 int 的“序数”,因此实现很简单:switch (someEnum) { case ENUMVAL: } 是 switch(someEnum.ordinal()) { case 1: } 周围的轻糖,其中 1 是 ENUMVAL 的序数值。
仅半近期:字符串
在 JDK7 中,添加了字符串切换支持。相同的原则适用:只有常量,null 是不行的 - 它是“快速”的,因为它实现为一个跳转表(与一系列 if/elseif 语句相反,它需要对每个语句进行比较。一个跳转表只是跳转,使用单个比较,直接跳到右边,如果没有匹配到最后。
现在的新东西 (12+):作为表达式
这些天 switch 可以是一个表达式。如,开关返回一个东西。但是,为了使这项工作正常进行,整个 switch 中的每条“路径”都必须返回一些东西,而且不可能是缺少 case 语句的情况(呵呵);毕竟,表达式应该解决什么问题呢?因此,在这种“模式”中,您一定已经用尽了所有选项。有趣的子案例是枚举,您可以在其中“穷举”,但在运行时这永远无法保证;如果有人向枚举添加一个值并重新编译只是枚举并将其放入其中怎么办?现在,以前的详尽(涵盖所有选项)开关不再是:
int y = switch(textyNumber) {
case "one": yield 1;
case "two": yield 2;
default: yield 0;
};
yield 是这里的一种新方法。这很像“返回”——它从开关“返回”,返回那个值。如果缺少该默认子句,则会出现编译器错误。
枚举的奇怪之处在于,如果你穷举(覆盖每个枚举值),它会编译,但如果你添加一个枚举值,不要使用开关重新编译代码,并尝试折腾一个新创建的枚举值通过,开关会抛出一个java.lang.IncompatibleClassChangeError。你知道的越多。
箭头语法
因为 'yield' 有点笨拙,而且它可能很有用,只需将所需的表达式单线化,类似于您可以编写的方式,例如:
Comparator<String> byLength = (a, b) -> a.length() - b.length();
同样的箭头语法现在在 switch 中是合法的,为了保持一致性,它甚至在语句形式的 switch 中也是合法的:
int x = 1;
int y = switch(x) {
case 1 -> 2;
default -> 0;
};
switch (y) {
case 0 -> System.out.println("Interesting");
}
箭头防止掉线(它暗示break),并且只接受一个语句。但是一整块也算一个语句:
switch (y) {
case 0 -> {
System.out.println("one");
System.out.println("two");
}
case 1 -> {
System.out.println("three");
}
}
只会打印“一”、“二”。
多案例
逗号可用于分隔值;这类似于您如何使用条形分隔异常类型(catch (IOException | SQLException e) { ... }。适用于所有表单。
模式匹配
现在我们来看看真正有趣的东西:
record Point(int x, int y) {}
...
Object p = new Point(10, 20);
switch (p) {
case Point(x, y) -> System.out.printf("Point [%d, %d]", x, y);
}
很快就会合法java了!现在,“案例”之后的东西是“模板”。你能把 p 解析到的任何东西粉碎到这个模板中吗?如果是,则大小写匹配,并为您填写变量。这适用于“可解构”类型(目前:仅记录;将来任何类型都可以提供解构函数),以及类似交易的 instanceof:
Object o = someExpression;
switch (o) {
case Number n -> System.out.println("is a number. intvalue: " + n.intValue());
case String s -> System.out.println("is a string: " + s.toLowerCase());
}
我认为这涵盖了所有可用于切换到 java v 17 甚至更高版本的更新。
注意:您可以通过关注相应的 openjdk 开发邮件列表,尽可能早地找到此信息。这些东西主要在 amber-dev 上找到,由于与记录密切相关,在 valhalla-dev 上可以找到一些模式匹配的东西,并且记录与值类型重叠(valhalla = java 的值类型,amber = 通用语言更新)。在 Java 会议上,通常有人会详细说明它的位置,reddit 的 /r/java 倾向于在 OpenJDK 团队的某个人(对于这些东西,你通常在看 Brian Goetz)发布有关该功能如何进行的重大更新时发布它设想/已实施。在 Twitter 上关注 Brian,这也有帮助。发行说明以及与任何新的 Java 版本相关的 JEP 总括页面也应该提及这些内容,并带有您可以访问的链接。