为什么我不能在字符串上使用 switch 语句?

2022-08-31 03:58:19

此功能是否会被放入更高的 Java 版本中?

有人可以解释为什么我不能这样做,就像Java语句的技术工作方式一样吗?switch


答案 1

带有案例的 Switch 语句已在 Java SE 7 中实现,至少在首次请求它们 16 年后。没有提供延迟的明确原因,但可能与性能有关。String

在 JDK 7 中的实现

该功能现在已经通过“去糖化”过程实现;在编译时,在声明中使用常量的干净,高级语法在编译时扩展到遵循模式的更复杂的代码中。生成的代码使用始终存在的 JVM 指令。javacStringcase

在编译过程中,将 a 转换为两个开关。第一个将每个字符串映射到一个唯一的整数,即它在原始开关中的位置。这是通过首先打开标签的哈希代码来完成的。相应的情况是测试字符串相等性的语句;如果哈希值上存在冲突,则测试是级联的 。第二个开关在原始源代码中镜像,但用其相应位置替换案例标签。这种两步过程使得保持原始开关的流量控制变得容易。switchStringifif-else-if

JVM 中的交换机

有关 的技术深度,可以参考 JVM 规范,其中描述了 switch 语句的编译。简而言之,有两种不同的JVM指令可用于交换机,具体取决于案例使用的常量的稀疏性。两者都依赖于对每个案例使用整数常量来有效执行。switch

如果常量是密集的,则将它们用作指令指针表(指令)的索引(减去最小值后)。tableswitch

如果常量稀疏,则执行对正确大小写的二进制搜索 - 指令。lookupswitch

在对物体进行糖化时,可能会使用这两种指令。适用于第一次打开哈希码以查找案例的原始位置。得到的序数是 .switchStringlookupswitchtableswitch

这两条指令都要求在编译时对分配给每个事例的整数常量进行排序。在运行时,虽然性能通常看起来比性能好,但需要一些分析来确定表是否足够密集,以证明空间-时间权衡的合理性。Bill Venners写了一篇很棒的文章,更详细地介绍了这一点,并在下面介绍了其他Java流控制指令。O(1)tableswitchO(log(n))lookupswitch

在 JDK 7 之前

在 JDK 7 之前,可以近似于基于交换机的开关。这使用编译器在每个类型上生成的静态值Of方法。例如:enumStringenum

Pill p = Pill.valueOf(str);
switch(p) {
  case RED:  pop();  break;
  case BLUE: push(); break;
}

答案 2

如果在代码中有一个位置可以打开 String,则最好将 String 重构为可能值的枚举,您可以打开该值。当然,您可以将字符串的潜在值限制为枚举中的字符串,这可能是也可能不是必需的。

当然,你的枚举可以有一个“other”的条目,以及一个 fromString(String) 方法,那么你可以有

ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
   case MILK: lap(); break;
   case WATER: sip(); break;
   case BEER: quaff(); break;
   case OTHER: 
   default: dance(); break;
}