在Java 7中打开字符串有什么好处?


19

当我开始用Java编程时,switch语句不带字符串的事实使我感到沮丧。然后,在使用Enums时,我意识到与它们相比,您所获得的好处而不是传递原始值-类型安全性(这使得重构更容易),同时也使其他开发人员更清楚。

我正在努力思考一种情况,在SE7中,我现在决定使用带有字符串而不是Enums作为输入的开关。如果通过打开整个字符串(例如,而不是部分匹配或正则表达式匹配)来实现它们,则似乎并没有提供更少的理由来更改代码。

借助IDE工具和编码的读写比率,我会比传递字符串值更快乐地自动生成一个额外的Enum。

他们作为程序员给我们带来什么好处?更少的样板?

似乎该功能并未让该语言大声疾呼。尽管可能有一个我忽略的用例。


字符串已经在switch语句中被大量使用,但是由于人们无法直接使用它们,因此他们采取了诸如巨树(如果不是树)或枚举转换的变通方法。两种解决方法都使代码的可读性降低。两种方法都是解决方法,如果本地实施的解决方案能够更好地说明程序员的意图,为什么还要使用解决方法。
Pieter B

@PieterB但是,可以说,枚举是在添加语义值而不是变通办法,这带来了类型安全性(例如,拼写错误将在编译时被捕获而不是导致错误)。它还允许我们重构该上下文中使用的字符串的所有实例,而不会影响该字符串的其他用法(这在域对象中可能经常发生。)
anotherdave

Answers:


12

据我所知,问题是为什么将String常量包装到enum中并不能满足语言用户的需求。在JDK 7(项目硬币)邮件列表中宣布的官方功能建议中已解决了此问题。

根据我对提案的阅读,基于枚举类型过大而拒绝了使用枚举的替代方法。为了您的方便,该提案的相关部分引用如下,以解决引用枚举声明大胆

主要优势:是什么使提案产生了有利的变化?

可以将更规则的编码模式用于基于一组恒定字符串值选择的操作;新构造的含义对于Java开发人员应该是显而易见的。

主要好处:如果采纳该建议,为什么平台会更好?

基于字符串的调度代码的性能可能更高。

主要缺点:总有成本。

某些增加了编译器的实现和测试复杂性。

替代品:在不更改语言的情况下,能否具有某种优势和优势?

没有; 链式if-then-else字符串相等性测试可能很昂贵,并且为其可切换常量引入枚举(每个感兴趣的字符串值一个)会在程序中添加另一种类型,而没有充分的理由 ...


粗体部分非常令人惊讶
Simon Bergot

1
@Simon对我个人来说很好,它读作是间接对待,“如果我们继续坚持Java5之前的方式来处理此类问题,我们将失去对C#的竞争”。:)对于“预Java5的方式”的一个例子,是指JDK-1223179:开关串 -提交于1995年,迅速关闭是不会解决:“不要屏住呼吸没有象这是我们的计划。 ”。
蚊蚋

谢谢,非常有兴趣了解提案中提出的理由。说我们在“没有充分的理由”的情况下向程序中引入类型仍然是错误的-我们是OO程序员!:)
anotherdave

5
@anotherdave“面向对象”只有在有意义的目的时才引入类型。显然,在JCP流行的观点证明,使用枚举作为愚笨包装在开关字符串常量没有资格作为有意义的
蚊蚋

1
哇,我很惊讶(以一种很好的方式)看到官方提案积极地对抗类型膨胀。令人耳目一新。在Java中,类型膨胀是一个巨大的问题。
本李

7

除了使代码更具可读性之外,与if/else if比较相比,还可能提高性能。更改是否值得,取决于您将进行多少次比较。字符串开关将作为两个单独的开关指令发出。第一个操作对哈希码进行操作,因此它通常是lookupswitch,最终会产生O(log n)复杂性。第二个总是完美的O(1) tableswitch,因此仍然复杂O(log n)。单个线性的if/else if语句链将使O(n)复杂性稍差一些。

如果您要比三个字符串进行比较,那么a switch可能更具可读性和紧凑性。它可能会更好地执行,尽管除非您在热代码路径上进行大量比较,否则您不太可能注意到差异。


1
我是在问为什么我要在字符串上使用开关而不是在枚举上使用开关。我认为大块的if/else做法很糟糕,但是目前,我没有太多理由使用它。
anotherdave

但是,然后将Map与命令模式结合使用,即使具有相同的复杂度,也可以提供更高的可读性,因为您拥有命令的额外类型
SpaceTrucker 2013年

3

当您的enum值来自外部(即存储在数据库中)时,可以使用字符串切换。

JDK 7中的另一件值得注意的事情switch是,它的性能远胜于if-else构造。

switch当您需要对节点和属性类型进行大量切换时,快速字符串es的一个很好的用例就是JSON / XML流解析。我想不到任何更好的选择。


1
“当您的枚举值来自外部时(即存储在数据库中),字符串切换是不可替代的。”:您能详细说明一下吗?switch语句中的字符串是硬编码的:一旦数据库中的字符串被更改,您的代码就会过期。
Giorgio

好吧,您是正确的- enum由于Java的静态特性,在这种情况下可以使用s。在写这篇文章时,我在想Role一个安全库中的一个,该库通常作为类提供,不能放入enum。另一种情况是动态编译的Java / Groovy代码-在这种情况下(这很罕见),字符串切换可能是一个更好的选择。
Andrey Chaschev

2

简单

Switch中的字符串支持可用于处理数据而无需转换为枚举或if-else逻辑。有时候打开String会更容易。

JDK 7(项目硬币)邮件列表中的功能建议@gnat答案

为它的可切换常量引入一个枚举,每个感兴趣的字符串值一个,将在没有充分原因的情况下向程序添加另一种类型...

If-Else版本

这很短,但是很多if's都很难阅读。这很慢。

if (color.equals("red")) {
    System.out.println("Color is Red");
} else if (color.equals("green")) {
    System.out.println("Color is Green");
} else {
    System.out.println("Color not found");
}

枚举版本

需要定义枚举,这很好,但是有时不需要。

enum Color {RED, GREEN}

照常处理

try {
    switch (Color.valueOf(color)) {
        case RED:
            System.out.println("Color is Red");
            break;
        case GREEN:
            System.out.println("Color is Green");
            break;
    }
} catch (IllegalArgumentException e) {
    System.out.println("Color not found");
}

JDK 7-switch语句版本中的字符串

我们可以处理而无需转换和定义其他类型。

switch (color) {
    case "red":
        System.out.println("Color is Red");
        break;
    case "green":
        System.out.println("Color is Green");
        break;
    default:
        System.out.println("Color not found");
}

7
但是,这并没有解决问题:为什么在这里使用字符串而不是枚举?
戴夫·牛顿

0

任何语言的大多数改进都是使代码更易于阅读。我比任何时候都喜欢可读的代码。

您可能不相信这一点,但可以尝试:

public enum JettStaff {
  ADRIAN("Adrian German") {
    public String toString() {
      return name + " (dgerman@indiana.edu)";
    }
  },
  ARJIT("Arjit Sengupta") {
    public String toString() {
      return name + " (asengupt@indiana.edu)";
    }
  },

  // and on for the rest...

  private String name;

  public JettStaff(String n) { this.name = n; }
}

JettStaff x = JettStaff.SUZANNE;
System.out.println(x);

我不清楚您的代码示例与上面的注释。您是用枚举作为难以理解的例子吗?
anotherdave


2
这是非常人为的。您假装该枚举没有电子邮件字段,或者这不是一个类。
戴夫牛顿

4
@ user2860598如果您想提出一点,请使用一个相关的示例。它也没有解决胖手指字符串常量。
戴夫牛顿

1
@ user2860598您链接的答案是说,已经引入了该功能,以前可以用枚举“近似”它们。我的观点是,即使引入了Enum,似乎还是更可取。
anotherdave

0

枚举很棒,只要有能力,就应该使用那些而不是字符串。但是有些时候您只是做不到,例如,当您需要处理来自Java外部的某些外部对象时。假设您必须解析某些内容。像服务器响应,配置,日志文件或类似的内容:您正在寻找许多选项,而这些选项根本不可能成为枚举。因此,在Java <7中,您会陷入一堆

  if (something.equals("foo")) {
    // foo processing
  } else 
  if (something.equals("bar")) {
   // bar processing
  }

在这种情况下,您仍然可以通过尝试按名称和/或通过提供自定义的String-> Enum例程来获取枚举,但有时这是不可能的或不切实际的(例如,当您必须创建太多枚举时)

TLDR:纯粹使用Java代码时,绝对应该使用Enums,但是使用外部对象并不总是可能。希望我的解释有意义。


如果你正在处理之外的数据,你已经知道如何知道你在你所需要的if报表,那么你应该包裹起来反正,这意味着你可以继续用枚举,或命令模式,等等
戴夫·牛顿

@DaveNewton是的,您仍然可以使用枚举,但是正如我在回答中所述,有时候这是不实际的,就像万一您最终创建一大堆枚举只能在解析期间在您的代码中使用过一样。

@dimoniy但是,如果您必须创建100个Enum值,这是否不意味着使用switch选项,您将必须具有100个case语句?如果有那么多变量,我绝对会更喜欢枚举的类型安全性。
anotherdave

0

我不同意这样的观点,即Strings在switch语句中使用它时,它的代码更简洁易读,并认为这是不好的编程习惯。如果更改用于存储的值,则必须在每次开关或if-elseif出现时更改它。这不好,因为您要将硬编码的值放在代码的每个目录中。如果有一天您决定更改这些硬编码值之一,该怎么办?搜索并替换每个副本?

如果您有一些使用if-elseif语句执行的硬编码值,则在Java 1.5之前,为它们使用常量原始值会更好,因为它带来了类型安全性。

好的,在某些情况下,您将获得String值(来自HTTP请求,文件等),并将其转换为原始值是很痛苦的。但是Enum在这一点上做得很好。因为当您想更改存储在Enum中的值时,只需在声明时更改它即可。无需更改代码中的其他任何内容。(您当然需要更改存储在其他位置的值)。

当然,如果您只是实现脏的和懒惰的代码,那是完美的。您不想涉及编写很多Enums,但是会使用大型复杂软件杀死您。


-1

如果您知道传入的信息,并且可能只有5个州,请使用Enum。但是,如果可能的状态可能超过9000,而您只需要查找42,则使用开关会更好,因为您不想键入所有这些状态。

在大多数情况下,枚举通常是大多数情况下的选择,除非可能的状态未知或有很多,而您只关心少数几个。

但是为什么现在要介绍呢?这只是一个允许使用更干净代码的更改。

在1.5中,它们还允许您为枚举创建自定义主体。


但是,您是否不只是将其他8958个可能的值分配给一个Enum状态?
anotherdave 2013年

@anotherdave这就是defaultswitch也发挥了作用。我不知道您可以使用设置默认状态Enum,除非您设置了默认状态,但是此代码只会变得肿,而使用switch起来更简洁。

3
UNKNOWN枚举上的状态是肿的,但default在开关上的情况却不是,这似乎很奇怪。
anotherdave 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.