协慌网

登录 贡献 社区

为什么我不能在 String 上使用 switch 语句?

这个功能是否会被放入以后的 Java 版本中?

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

答案

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

在 JDK 7 中实现

该功能现已在javac实现, 具有 “去糖” 过程;一个干净的高级语法,使用String常量, case声明在编译时扩展为模式后面的更复杂的代码。生成的代码使用始终存在的 JVM 指令。

在编译期间,具有String情况的switch被转换为两个开关。第一个将每个字符串映射到一个唯一的整数 - 它在原始开关中的位置。这是通过首先打开标签的哈希码来完成的。相应的情况是测试字符串相等性的if语句; 如果哈希上有冲突,则测试是级联if-else-if 。第二个开关镜像原始源代码中的那个,但用它们对应的位置替换案例标签。这个两步过程可以很容易地保留原始开关的流量控制。

在 JVM 中切换

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

如果常量是密集的,则它们被用作索引(在减去最低值之后)到指令指针表中 - tableswitch指令。

如果常量是稀疏的,则执行二进制搜索正确的情况 - lookupswitch指令。

在脱糖一个switchString对象,两者的指令有可能被使用。 lookupswitch适用于第一次切换哈希码以查找案例的原始位置。由此产生的序数非常适合tableswitch

两个指令都要求在编译时对分配给每个案例的整数常量进行排序。在运行时,而O(1)性能tableswitch普遍显得比更好的O(log(n))性能lookupswitch ,它需要一些分析,以确定该表是否足够密集证明时空权衡。 Bill Venners 写了一篇很棒的文章 ,详细介绍了这一点,以及其他 Java 流程控制指令的内幕。

在 JDK 7 之前

在 JDK 7 之前, enum可以近似基于String的开关。这使用编译器为每个enum类型生成的静态valueOf方法。例如:

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

如果您在代码中有一个可以打开 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;
}

以下是基于 JeeBee 帖子的完整示例,使用 java enum 而不是使用自定义方法。

请注意,在 Java SE 7 及更高版本中,您可以在 switch 语句的表达式中使用 String 对象。

public class Main {

    /**
    * @param args the command line arguments
    */
    public static void main(String[] args) {

      String current = args[0];
      Days currentDay = Days.valueOf(current.toUpperCase());

      switch (currentDay) {
          case MONDAY:
          case TUESDAY:
          case WEDNESDAY:
              System.out.println("boring");
              break;
          case THURSDAY:
              System.out.println("getting better");
          case FRIDAY:
          case SATURDAY:
          case SUNDAY:
              System.out.println("much better");
              break;

      }
  }

  public enum Days {

    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
  }
}