首页 文章

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

提问于
浏览
943

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

有人可以解释为什么我可以't do this, as in, the technical way Java' s switch 声明有效吗?

14 回答

  • 89
    public class StringSwitchCase { 
    
        public static void main(String args[]) {
    
            visitIsland("Santorini"); 
            visitIsland("Crete"); 
            visitIsland("Paros"); 
    
        } 
    
        public static void visitIsland(String island) {
             switch(island) {
              case "Corfu": 
                   System.out.println("User wants to visit Corfu");
                   break; 
              case "Crete": 
                   System.out.println("User wants to visit Crete");
                   break; 
              case "Santorini": 
                   System.out.println("User wants to visit Santorini");
                   break; 
              case "Mykonos": 
                   System.out.println("User wants to visit Mykonos");
                   break; 
             default: 
                   System.out.println("Unknown Island");
                   break; 
             } 
        } 
    
    }
    
  • 25

    除了上面的好论点之外,我还要补充一点,今天很多人都认为 switch 是Java过程中过时的一部分(回到C时代) .

    我不完全赞同这个观点,我认为 switch 在某些情况下可以有用,至少因为它的速度,而且无论如何它比一些级联数字更好......我在一些代码中看到了......

    但实际上,值得研究一下你需要一个开关的情况,并看看它是否不能被更多的OO取代 . 例如Java 1.5中的枚举,也许是HashTable或其他一些集合(有时我很遗憾我们没有(匿名)函数作为一等公民,如Lua - 没有switch或JavaScript)甚至多态 .

  • 12

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

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

    预处理文件名为Foo.jpp,并使用ant脚本处理到Foo.java中 .

    优点是它被加工成在1.0上运行的Java(尽管通常我们只支持回1.4) . 与使用枚举或其他变通方法捏造它相比,这样做(很多字符串切换)要容易得多 - 代码更容易阅读,维护和理解 . IIRC(此时无法提供统计数据或技术推理)它也比自然Java等价物更快 .

    缺点是你没有编辑Java所以它的工作流程更多(编辑,处理,编译/测试),而且IDE会链接回Java,这有点复杂(交换机变成了一系列if / else逻辑步骤)并且不维护开关盒顺序 .

    我不推荐它用于1.7但是如果你想编写针对早期JVM的Java(因为Joe public很少安装最新版本),它会很有用 .

    你可以得到from SVN或浏览code online . 你需要EBuild按原样构建它 .

  • -4

    以下是基于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
      }
    }
    
  • 120

    不是很漂亮,但这是Java 6和下面的另一种方式:

    String runFct = 
            queryType.equals("eq") ? "method1":
            queryType.equals("L_L")? "method2":
            queryType.equals("L_R")? "method3":
            queryType.equals("L_LR")? "method4":
                "method5";
    Method m = this.getClass().getMethod(runFct);
    m.invoke(this);
    
  • 18

    在Groovy中轻而易举;我嵌入了groovy jar并创建了一个 groovy 实用程序类来完成所有这些以及我发现在Java中令人生气的事情(因为我在企业中使用Java 6时被困) .

    it.'p'.each{
    switch (it.@name.text()){
       case "choclate":
         myholder.myval=(it.text());
         break;
         }}...
    
  • 18

    James Curran简洁地说:“基于整数的开关可以优化为非常高效的代码 . 基于其他数据类型的开关只能编译为一系列if()语句 . 因此,C&C只允许在整数类型上切换,因为它与其他类型毫无意义 . “

    我认为,只有这样,一旦你开始转换非原语,你需要开始考虑“等于”与“==” . 首先比较两个字符串可能是一个相当冗长的过程,增加了上面提到的性能问题 . 其次,如果有字符串切换,将需要切换字符串忽略大小写,切换字符串考虑/忽略语言环境,根据正则表达式切换字符串....我会赞成一个为节省大量时间的决定语言开发人员花费了少量时间供程序员使用 .

  • 967

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

    如果您不使用JDK7或更高版本,则可以使用 hashCode() 来模拟它 . 因为 String.hashCode() 通常为不同的字符串返回不同的值,并且总是为相等的字符串返回相等的值,所以它相当可靠(不同的字符串可以生成与注释中提到的@Lii相同的哈希码,例如 "FB""Ea" )请参阅documentation .

    所以,代码看起来像这样:

    String s = "<Your String>";
    
    switch(s.hashCode()) {
    case "Hello".hashCode(): break;
    case "Goodbye".hashCode(): break;
    }
    

    这样,你在技术上开启 int .

    或者,您可以使用以下代码:

    public final class Switch<T> {
        private final HashMap<T, Runnable> cases = new HashMap<T, Runnable>(0);
    
        public void addCase(T object, Runnable action) {
            this.cases.put(object, action);
        }
    
        public void SWITCH(T object) {
            for (T t : this.cases.keySet()) {
                if (object.equals(t)) { // This means that the class works with any object!
                    this.cases.get(t).run();
                    break;
                }
            }
        }
    }
    
  • -7

    也可以显示自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;
        }
    
    }
    
  • 4

    其他答案已经说过这是在Java 7中添加的,并为早期版本提供了解决方法 . 这个答案试图回答“为什么”

    Java是对C的过度复杂性的反应 . 它被设计成一种简单干净的语言 .

    String在语言中得到了一些特殊的案例处理,但我似乎很清楚,设计师们试图保持特殊外壳的数量和语法糖降到最低 .

    因为字符串不是简单的原始类型,所以在字符串之间切换是相当复杂的 . 它在Java设计时并不常见,并且与极简主义设计并不完全吻合 . 特别是因为他们已经决定不对字符串特殊情况==,对于在==没有的情况下工作的情况会是(并且)有点奇怪 .

    在1.0到1.4之间,语言本身几乎保持不变 . Java的大多数增强功能都在库的一面 .

    这一切都随着Java 5而改变,语言得到了大幅扩展 . 在版本7和版本8中进一步扩展 . 我希望这种态度的改变是由C#的兴起所驱动的

  • 4

    具有 String 案例的切换语句已在Java SE 7中实施,至少16年after they were first requested.未提供延迟的明确原因,但可能与性能有关 .

    在JDK 7中实现

    该功能现在已在 javac with a "de-sugaring" process;中实现了一个干净的高级语法,在 case 声明中使用 String 常量,在编译时将其扩展为模式后面的更复杂的代码 . 生成的代码使用始终存在的JVM指令 .

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

    在JVM中切换

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

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

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

    在对 String 对象上取消 switch 时,可能会使用这两个指令 . lookupswitch 适用于第一次开启哈希码以查找案例的原始位置 . 由此产生的序数非常适合 tableswitch .

    两个指令都要求在编译时对分配给每个案例的整数常量进行排序 . 在运行时,虽然 tableswitchO(1) 性能通常比 lookupswitchO(log(n)) 性能好,但它需要进行一些分析以确定该表是否足够密集以证明时空权衡的合理性 . Bill Venners写了a great article,更详细地介绍了这一点,以及其他Java流程控制指令的内幕 .

    在JDK 7之前

    在JDK 7之前, enum 可以接近基于 String 的开关 . 这使用编译器在每个 enum 类型上生成的the static valueOf方法 . 例如:

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

    当你使用intellij时也要看:

    文件 - >项目结构 - >项目

    文件 - >项目结构 - >模块

    如果有多个模块,请确保在模块选项卡中设置正确的语言级别 .

  • 8

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

    因此,C&C只允许对整数类型进行切换,因为它对其他类型没有意义 .

    C#的设计师认为风格很重要,即使没有优势 .

    Java的设计者显然像C的设计师一样 .

相关问题