这个功能会被放到后面的Java版本中吗?

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


当前回答

多年来,我们一直在为此使用(n个开源)预处理器。

//#switch(target)
case "foo": code;
//#end

预处理文件被命名为Foo.jpp,并使用ant脚本处理为Foo.java。

优点是它被处理成运行在1.0上的Java(尽管通常我们只支持1.4版本)。而且,与使用枚举或其他变通方法相比,这样做(大量字符串开关)要容易得多——代码更容易阅读、维护和理解。IIRC(在这一点上不能提供统计数据或技术推理)也比自然的Java等价物更快。

缺点是你没有编辑Java,所以它有更多的工作流(编辑、处理、编译/测试),加上IDE将链接回Java,这有点复杂(切换变成一系列if/else逻辑步骤),并且切换case顺序没有维护。

对于1.7以上版本,我不推荐使用它,但如果您想编写针对早期jvm的Java程序,它很有用(因为Joe public很少安装最新版本)。

您可以从SVN获取或在线浏览代码。您将需要EBuild按原样构建它。

其他回答

下面是一个基于JeeBee的帖子的完整示例,使用java枚举而不是使用自定义方法。

注意,在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
  }
}

带有String情况的Switch语句已经在Java SE 7中实现,距离它们第一次被请求至少有16年了。目前还没有给出延迟发布的明确原因,但可能与性能有关。

JDK 7中的实现

这个特性现在已经在javac中通过“去糖化”过程实现了;在case声明中使用String常量的干净的高级语法在编译时扩展为遵循模式的更复杂的代码。生成的代码使用一直存在的JVM指令。

A switch with String cases is translated into two switches during compilation. The first maps each string to a unique integer—its position in the original switch. This is done by first switching on the hash code of the label. The corresponding case is an if statement that tests string equality; if there are collisions on the hash, the test is a cascading if-else-if. The second switch mirrors that in the original source code, but substitutes the case labels with their corresponding positions. This two-step process makes it easy to preserve the flow control of the original switch.

JVM中的开关

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

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

如果常量是稀疏的,则执行对正确情况的二进制搜索——查找开关指令。

在String对象的去糖开关中,两个指令都可能被使用。查找开关适用于哈希码的第一个开关,以查找案件的原始位置。由此产生的序数很自然地适合于大表女巫。

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

JDK 7之前

在JDK 7之前,enum可以近似于基于字符串的开关。它使用编译器在每个枚举类型上生成的静态valueOf方法。例如:

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

从1.7开始直接使用String的例子如下:

public static void main(String[] args) {

    switch (args[0]) {
        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;
    }

}

在Java 11+中,变量也是可行的。唯一的条件是它必须是常数。

例如:

final String LEFT = "left";
final String RIGHT = "right";
final String UP = "up";
final String DOWN = "down";

String var = ...;

switch (var) {
    case LEFT:
    case RIGHT:
    case DOWN:
    default:
        return 0;
}

PS.我还没有在早期的jdk中尝试过这一点。所以请更新答案,如果它也支持。

基于整数的开关可以优化为非常高效的代码。基于其他数据类型的开关只能编译为一系列if()语句。

因此,C和c++只允许对整型进行切换,因为它对其他类型没有意义。

c#的设计者认为风格很重要,即使它没有什么优势。

Java的设计者显然和C的设计者思想相似。