可以用全大写字母命名枚举来简化其String表示吗?


14

几次我见过人们对枚举常量使用标题大小写或什至所有小写字母命名,例如:

enum Color {
  red,
  yellow,
  green;
}

throw new IllegalStateException("Light should not be " + color + ".")例如,如果要这样做,这使得使用它们的字符串形式变得简单容易。

如果枚举是private,这似乎可以接受,但我仍然不喜欢它。我知道我可以使用String字段创建一个枚举构造函数,然后重写toString以返回该名称,如下所示:

enum Color {
  RED("red"),
  YELLOW("yellow"),
  GREEN("green");

  private final String name;

  private Color(String name) { 
    this.name = name 
  }

  @Override public String toString() {
    return name; 
  }
}

但是,看那还要多久。如果您有一堆小的枚举要保持简单,那么继续这样做很烦人。可以在这里只使用非常规大小写格式吗?


4
这是一个惯例。您有违反常规的理由吗?随你(由你决定。

9
只是想知道出现异常消息怎么了Light should not be RED.?毕竟,此消息是针对开发人员的,用户永远都不会看到它。
布兰丁

1
@Brandin如果颜色更多是实现细节,那么没人会看到它。当然,那么我想这就是为什么我们需要一个很好的toString形式的问题。
密码破解者

8
归根结底,这取决于您。如果我正在调试您的代码并看到异常消息,Light should not be YELLOW.我猜它是某种常量,则toString()方法仅用于调试目的,因此我看不到为什么需要更改它在该消息中的显示方式。
布兰丁

1
我认为答案更多是基于您的“好”的想法。如果这样做,世界极有可能不会大火,甚至可以成功编写一个程序。它有用吗?嗯,太主观了。如果您获得了一些好处,那对您来说是否值得,会影响其他人吗?这些是我要关心的问题。
加雷特·克拉伯恩(Garet Claborn)

Answers:


14

简短的答案是,当然,您是否想要打破本质上恒定的命名约定...从JLS引用:

常量名称

接口类型中常量的名称应该是,而类类型的最终变量通常可以是一个或多个单词,首字母缩写词或缩写的序列,全部为大写,并用下划线“ _”字符分隔。常数名称应为描述性名称,而不必缩写。按照惯例,它们可以是任何适当的语言部分。

关于的长答案toString()是,如果您想要更可读enum值表示形式,那么绝对是重写此方法。引用自Object.toString()(强调我的):

返回对象的字符串表示形式。通常,该toString方法返回一个“以文本形式表示”此对象字符串。结果应该是简洁易懂的表示形式,使人易于阅读。建议所有子类都重写此方法。

现在,我不确定为什么有些答案会转移到谈论enumsString值来回转换的问题,但是我也将在这里给出我的看法。enum通过使用name()ordinal()方法,可以很容易地处理这些值的序列化。两者都是final,因此您可以确定返回的值,只要的名称或位置不变。对我来说,这是一个足够清晰的标记。

从以上内容我可以得出的是:今天,您可能希望将其描述YELLOW为简单的“黄色”。明天,您可能需要将其描述为“ Pantone Minion Yellow”。这些说明应从调用返回toString(),我也不希望name()ordinal()更改。如果这样做,那是我需要在我的代码库或团队中解决的问题,这不仅是enum命名样式而且是一个更大的问题

总之,如果你打算做的是记录一个更可读的代表性enum值,我还是会建议坚持公约,然后重写toString()。如果您还打算将它们序列化为数据文件或其他非Java目的地,那么您仍然可以使用name()ordinal()方法来进行备份,因此无需担心重写toString()


5

不要修改ENUM那只是不好的代码味道。设枚举为枚举,字符串为字符串。

而是使用CamelCase转换器获取字符串值。

throw new IllegalStateException("Light should not be " + CamelCase(color) + ".");

有许多开源库已经为Java解决了此问题。

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/CaseFormat.html


在某些极端情况下不是确定性的吗?(不是特定转换器的行为,而是一般原则)
Panzercrisis

-1添加第三方库,这可能不是基本String格式所需要的。
user949300

1
@ user949300复制代码,编写自己的代码或任何内容。您错过了重点。
Reactgular 2015年

您能否详细说明“重点”?“让一个枚举成为一个枚举,让一个字符串成为一个字符串”听起来不错,但这实际上是什么意思呢?
user949300

1
枚举提供类型安全性。他不能通过“马铃薯”作为颜色。而且,也许他在枚举中包含其他数据,例如thr RGB值,只是没有在代码中显示。有很多很好的理由使用枚举而不是字符串。
user949300

3

在Java代码与数据库或客户端应用程序之间发送枚举时,我常常最终将枚举值作为字符串读取和写入。 toString()连接字符串时隐式调用。在某些枚举上重写toString()意味着有时我可以

"<input type='checkbox' value='" + MY_CONST1 + "'>"

有时我不得不记得打电话

"<input type='checkbox' value='" + MY_CONST1.name() + "'>"

这导致了错误,所以我不再这样做了。实际上,我不会重写Enum上的任何方法,因为如果将它们扔给足够的客户端代码,最终将超出人们的期望。

设置自己的新方法名称,例如public String text()toEnglish()或其他。

如果您有很多上述枚举,这是一个小帮手功能,可以为您节省一些打字时间:

public static String ucFirstLowerRest(String s) {
    if ( (s == null) || (s.length() < 1) ) {
        return s;
    } else if (s.length() == 1) {
        return s.toUpperCase();
    } else {
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }
}

调用.toUpperCase()或.toLowerCase()总是很容易,但是要恢复混合大小写可能会很棘手。考虑一下颜色“法国蓝”。France始终是大写字母,因此如果遇到枚举,可能需要向您的枚举中添加一个textLower()方法。当您在句子的开头,句子的中间,标题中使用此文本时,您会发现单个toString()方法的不足之处。而且这甚至都不会触及Java标识符中非法的字符,或者因为在标准键盘上没有表示它们或没有大小写的字符(汉字等)而很难键入。

enum Color {
  BLEU_DE_FRANCE {
    @Override public String textTc() { return "Bleu De France"; }
    @Override public String textLc() { return "bleu de France"; }
  }
  CAFE_NOIR {
    @Override public String textTc() { return "Café Noir"; }
  }
  RED,
  YELLOW,
  GREEN;

  // The text in title case
  private final String textTc;

  private Color() { 
    textTc = ucFirstLowerRest(this.toString());
  }

  // Title case
  public String textTc() { return textTc; }

  // For the middle of a sentence
  public String textLc() { return textTc().toLowerCase(); }

  // For the start of a sentence
  public String textUcFirst() {
    String lc = textLc();
    return lc.substring(0, 1).toUpperCase() + lc.substring(1);
  }
}

正确使用它们并不难:

IllegalStateException(color1.textUcFirst() + " clashes horribly with " +
                      color2.textLc() + "!")

希望这也说明了为什么使用大小写混合的枚举值也会令您失望。保持所有大写字母都带有下划线的枚举常量的最后一个原因是遵循最小惊讶原则。人们期望如此,因此,如果您做不同的事情,您总是必须要自我解释,或者与滥用代码的人打交道。


3
对于toString()一个枚举永远不要重写的建议对我来说毫无意义。如果要确保枚举名称的默认行为,请使用name()。我完全没有依靠toString()来为提供正确的密钥是“诱人” 的valueOf(String)。我想如果您想用白痴来证明您的枚举,那是一回事,但是我认为这不是一个足够好的理由,建议您不要重写toString()
密码破解者

1
此外,我发现了这一点,其中建议(因为我被带领相信)上枚举覆盖的toString,其实是一件好事:stackoverflow.com/questions/13291076/...
密码破译

连接字符串时会隐式调用@codebreaker toString()。在某些枚举上重写toString()意味着有时我可以just <input type='checkbox' value=" + MY_CONST1 + ">,有时我必须记住调用<input type='checkbox' value=" + MY_CONST1.name() + ">,这会导致错误。
GlenPeterson

好的,这很不错,但是仅当您使用其名称“枚举”枚举时才真正重要(这不是我的示例所要担心的)。
密码破解者

0

如果将这些字符串表示形式存储在数据库或文本文件等位置的可能性很小,那么如果您需要重构枚举,则将它们与实际的枚举常量绑定在一起将变得非常棘手。

如果有一天您决定重命名Color.White为,Color.TitaniumWhite而其中存在包含“ White”的数据文件怎么办?

另外,一旦开始将枚举常量转换为字符串,下一步便是生成供用户查看的字符串,这里的问题是,正如我已经确定的那样,java的语法没有在标识符内允许有空格。(您永远不会拥有Color.Titanium White。)因此,由于您可能需要一种适当的机制来生成要显示给用户的枚举名称,因此最好避免不必要地使您的枚举复杂化。

话虽这么说,您当然可以继续做您一直想做的事情,然后在以后(如果有麻烦的话)将重构的枚举传递给构造函数,以将名称传递给构造函数。

您可以通过声明name字段public final并丢失getter来减少带有构造函数的枚举的几行内容。(Java程序员对吸气剂的这种爱是什么?)


将公共最终静态变量编译为将其用作实际常量,而不是对其来源类的引用的代码。在进行部分代码重新编译(或替换jar)时,这可能导致许多其他有趣的错误。如果您建议public final static int white = 1;public final static String white = "white";(除了再次违反约定),这会失去枚举提供的类型安全性。

@MichaelT枚举字段不是静态的。为每个枚举常量创建一个实例,该枚举的成员是该实例的成员变量。一public final,但未通过构造方法的行为完全像一个非最终场初始化实例字段,除非你被阻止在编译时从分配给它。您可以通过反射来修改它,引用它的所有代码都将立即开始看到新值。
Mike Nakis 2015年

0

可以说,遵循大写命名约定违反了DRY。(还有YAGNI)。例如,在您的代码示例#2中:重复“ RED”和“ red”。

那么,您遵循官方惯例还是遵循DRY?您的来电。

在我的代码中,我将遵循DRY。但是,我将对Enum类发表评论,说“不是所有大写的,因为(在此说明)”

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.